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
20 workdir
=> '/tmp/our-deploy',
28 my $DATA_POSITION = tell DATA
; # GLOBAL VARIABLE
29 my %script_config = (%default_config, get_config
());
31 my %config = %script_config;
32 if (! $config{passthrough
}) {
53 workdir|work-directory|deploy-directory|w=s
56 %config = (%config, %cmdline_config);
59 usage
() if $config{usage
};
60 version
() if $config{version
};
62 if ($config{roottar
}) {
64 my ($fh, $size) = locate_file
('root');
65 copy
($fh, \
*STDOUT
, $size);
67 } ## end if ($config{roottar})
69 if ($config{heretar
}) {
71 my ($fh, $size) = locate_file
('here');
72 copy
($fh, \
*STDOUT
, $size);
74 } ## end if ($config{heretar})
78 print {*STDOUT
} Data
::Dumper
::Dumper
(\
%script_config);
82 if ($config{inspect
}) {
84 $config{'deploy'} = 0;
85 $config{'tempdir'} = 0;
86 $config{workdir
} = $config{inspect
};
87 } ## end if ($config{inspect})
89 if ($config{dryrun
}) {
91 print {*STDOUT
} Data
::Dumper
::Dumper
(\
%config);
95 if ($config{filelist
}) {
96 my $root_tar = get_sub_tar
('root');
98 $root_tar->print_filelist();
99 my $here_tar = get_sub_tar
('here');
101 $here_tar->print_filelist();
103 } ## end if ($config{filelist})
105 # go into the working directory, creating any intermediate if needed
106 mkpath
($config{workdir
});
107 chdir($config{workdir
});
108 print {*STDERR
} "### Got into working directory '$config{workdir}'\n\n"
112 if ($config{'tempdir'}) { # Only if allowed
113 my $now = strftime
('%Y-%m-%d_%H-%M-%S', localtime);
115 tempdir
($now . 'X' x
10, DIR
=> '.', CLEANUP
=> $config{cleanup
});
118 or die "chdir('$tempdir'): $OS_ERROR\n";
120 if ($config{verbose
}) {
122 "### Created and got into temporary directory '$tempdir'\n";
123 print {*STDERR
} "### (will clean it up later)\n" if $config{cleanup
};
124 print {*STDERR
} "\n";
126 } ## end if ($config{'tempdir'})
128 eval { # Not really needed, but you know...
129 $ENV{PATH
} = '/bin:/usr/bin:/sbin:/usr/sbin';
131 execute_deploy_programs
() unless $config{'no-exec'};
133 warn "$EVAL_ERROR\n" if $EVAL_ERROR;
135 # Get back so that cleanup can successfully happen, if requested
136 chdir '..' if defined $tempdir;
142 seek $fh, $DATA_POSITION, SEEK_SET
;
144 chomp(my $sizes = <$fh>);
145 my ($name_size, $file_size) = split /\s+/, $sizes;
146 my $name = full_read
($fh, $name_size);
147 full_read
($fh, 1); # "\n"
148 return ($fh, $file_size) if $name eq $filename;
149 seek $fh, $file_size + 2, SEEK_CUR
; # includes "\n\n"
151 die "could not find '$filename'";
155 my ($fh, $size) = @_;
159 my $nread = read $fh, $buffer, $size;
160 die "read(): $OS_ERROR" unless defined $nread;
161 die "unexpected end of file" unless $nread;
169 my ($ifh, $ofh, $size) = @_;
172 my $nread = read $ifh, $buffer, ($size < 4096 ?
$size : 4096);
173 die "read(): $OS_ERROR" unless defined $nread;
174 die "unexpected end of file" unless $nread;
175 print {$ofh} $buffer;
183 my ($fh, $size) = locate_file
($filename);
184 return Deployable
::Tar
->new(%config, fh
=> $fh, size
=> $size);
185 } ## end sub get_sub_tar
188 my ($fh, $size) = locate_file
('config.pl');
189 my $config_text = full_read
($fh, $size);
190 my $config = eval 'my ' . $config_text or return;
191 return $config unless wantarray;
193 } ## end sub get_config
196 my $here_tar = get_sub_tar
('here');
197 $here_tar->extract();
199 my $root_dir = $config{inspect
} ?
'root' : '/';
200 mkpath
$root_dir unless -d
$root_dir;
203 my $root_tar = get_sub_tar
('root');
204 $root_tar->extract();
208 } ## end sub save_files
210 sub execute_deploy_programs
{
211 my @deploy_programs = @
{$config{deploy
} || []};
213 if ($config{bundle
}) { # add all executable scripts in current directory
214 print {*STDERR
} "### Auto-deploying all executables in main dir\n\n"
216 my %flag_for = map { $_ => 1 } @deploy_programs;
218 for my $item (sort readdir $dh) {
219 next if $flag_for{$item};
220 next unless ((-f
$item) || (-l
$item)) && (-x
$item);
221 $flag_for{$item} = 1;
222 push @deploy_programs, $item;
223 } ## end for my $item (sort readdir...
225 } ## end if ($config{bundle})
228 for my $deploy (@deploy_programs) {
229 $deploy = catfile
('.', $deploy)
230 unless file_name_is_absolute
($deploy);
232 print {*STDERR
} "### Skipping '$deploy', not executable\n\n"
236 print {*STDERR
} "### Executing '$deploy'...\n"
238 system {$deploy} $deploy, @ARGV;
241 } ## end for my $deploy (@deploy_programs)
244 } ## end sub execute_deploy_programs
247 my $progname = basename
($0);
248 print {*STDOUT
} <<"END_OF_USAGE" ;
250 $progname version $VERSION - for help on calling and options, run:
258 my $progname = basename($0);
259 print {*STDOUT} <<"END_OF_USAGE
" ;
260 $progname version $VERSION
262 More or less, this script is intended to be launched without parameters.
263 Anyway, you can also set the following options, which will override any
264 present configuration (except in "--show
-options
"):
266 * --usage | --man | --help
267 print these help lines and exit
270 print script version and exit
272 * --bundle | --all-exec | -X
273 treat all executables in the main deployment directory as scripts
276 * --cleanup | -c | --no-cleanup
277 perform / don't perform temporary directory cleanup after work done
279 * --deploy | --no-deploy
280 deploy scripts are executed by default (same as specifying '--deploy')
281 but you can prevent it.
283 * --dryrun | --dry-run
284 print final options and exit
286 * --filelist | --list | -l
287 print a list of files that are shipped in the deploy script
289 * --heretar | --here-tar | -H
290 print out the tar file that contains all the files that would be
291 extracted in the temporary directory, useful to redirect to file or
292 pipe to the tar program
294 * --inspect | -i <dirname>
295 just extract all the stuff into <dirname> for inspection. Implies
296 --no-deploy, --no-tempdir, ignores --bundle (as a consequence of
297 --no-deploy), disables --cleanup and sets the working directory
301 don't use system "tar
"
303 * --roottar | --root-tar | -R
304 print out the tar file that contains all the files that would be
305 extracted in the root directory, useful to redirect to file or
306 pipe to the tar program
308 * --show | --show-options | -s
309 print configured options and exit
311 * --tar | -t <program-path>
312 set the system "tar
" program to use.
314 * --tempdir | --no-tempdir
315 by default a temporary directory is created (same as specifying
316 '--tempdir'), but you can execute directly in the workdir (see below)
319 * --workdir | --work-directory | --deploy-directory | -w
320 working base directory (a temporary subdirectory will be created
328 print "$0 version
$VERSION\n";
333 package Deployable::Tar;
337 my $self = { ref $_[0] ? %{$_[0]} : @_ };
338 $package = 'Deployable::Tar::Internal';
339 if (! $self->{'no-tar'}) {
340 if ((exists $self->{tar}) || (open my $fh, '-|', 'tar', '--help')) {
341 $package = 'Deployable::Tar::External';
342 $self->{tar} ||= 'tar';
345 bless $self, $package;
346 $self->initialise() if $self->can('initialise');
350 package Deployable::Tar::External;
351 use English qw( -no_match_vars );
355 my $compression = $self->{bzip2
} ?
'j'
356 : $self->{gzip
} ?
'z'
358 $self->{_list_command
} = 'tv' . $compression . 'f';
359 $self->{_extract_command
} = 'x' . $compression . 'f';
364 open my $tfh, '|-', $self->{tar
}, $self->{_list_command
}, '-'
365 or die "open() on pipe to tar: $OS_ERROR";
366 main
::copy
($self->{fh
}, $tfh, $self->{size
});
372 open my $tfh, '|-', $self->{tar
}, $self->{_extract_command
}, '-'
373 or die "open() on pipe to tar: $OS_ERROR";
374 main
::copy
($self->{fh
}, $tfh, $self->{size
});
378 package Deployable
::Tar
::Internal
;
379 use English
qw( -no_match_vars );
384 my $data = main
::full_read
($self->{fh
}, $self->{size
});
385 open my $fh, '<', \
$data or die "open() on internal variable: $OS_ERROR";
387 require Archive
::Tar
;
388 $self->{_tar
} = Archive
::Tar
->new();
389 $self->{_tar
}->read($fh);
396 print {*STDOUT
} " $_\n" for $self->{_tar
}->list_files();
402 $self->{_tar
}->extract();