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 );
16 use Fcntl
qw( :seek );
18 # *** NOTE *** LEAVE EMPTY LINE ABOVE
20 my %default_config = ( # default values
21 workdir
=> '/tmp/our-deploy',
45 workdir|work-directory|deploy-directory|w=s
49 usage
() if $config{usage
};
50 version
() if $config{version
};
52 my $DATA_POSITION = tell DATA
;
55 my $TAR_PACKAGE; # FIXME
57 if ($config{roottar
}) {
59 my ($fh, $size) = locate_file
('root');
60 copy
($fh, \
*STDOUT
, $size);
62 } ## end if ($config{roottar})
64 if ($config{heretar
}) {
66 my ($fh, $size) = locate_file
('here');
67 copy
($fh, \
*STDOUT
, $size);
69 } ## end if ($config{heretar})
71 if ($config{filelist
}) {
72 my $root_tar = get_sub_tar
('root');
74 $root_tar->print_filelist();
75 my $here_tar = get_sub_tar
('here');
77 $here_tar->print_filelist();
79 } ## end if ($config{filelist})
81 my %script_config = (%default_config, get_config
());
84 print {*STDOUT
} Data
::Dumper
::Dumper
(\
%script_config);
88 # Merge configurations and go on
89 %config = (%script_config, %config);
91 if ($config{inspect
}) {
93 $config{'deploy'} = 0;
94 $config{'tempdir'} = 0;
95 $config{workdir
} = $config{inspect
};
96 } ## end if ($config{inspect})
98 if ($config{dryrun
}) {
100 print {*STDOUT
} Data
::Dumper
::Dumper
(\
%config);
104 # go into the working directory, creating any intermediate if needed
105 mkpath
($config{workdir
});
106 chdir($config{workdir
});
107 print {*STDERR
} "### Got into working directory '$config{workdir}'\n\n";
110 if ($config{'tempdir'}) { # Only if allowed
111 my $now = strftime
('%Y-%m-%d_%H-%M-%S', localtime);
113 tempdir
($now . 'X' x
10, DIR
=> '.', CLEANUP
=> $config{cleanup
});
117 "### Created and got into temporary directory '$tempdir'\n";
118 print {*STDERR
} "### (will clean it up later)\n" if $config{cleanup
};
119 print {*STDERR
} "\n";
120 } ## end if ($config{'tempdir'})
122 eval { # Not really needed, but you know...
123 $ENV{PATH
} = '/bin:/usr/bin:/sbin:/usr/sbin';
125 execute_deploy_programs
() if $config{'deploy'};
127 carp
$EVAL_ERROR if $EVAL_ERROR;
129 # Get back so that cleanup can successfully happen, if requested
130 chdir '..' if defined $tempdir;
136 seek $fh, $DATA_POSITION, SEEK_SET
;
138 chomp(my $sizes = <$fh>);
139 my ($name_size, $file_size) = split /\s+/, $sizes;
140 my $name = full_read
($fh, $name_size);
141 full_read
($fh, 1); # "\n"
142 return ($fh, $file_size) if $name eq $filename;
143 seek $fh, $file_size + 2, SEEK_CUR
; # includes "\n\n"
145 die "could not find '$filename'";
149 my ($fh, $size) = @_;
153 my $nread = read $fh, $buffer, $size;
154 die "read(): $OS_ERROR" unless defined $nread;
155 die "unexpected end of file" unless $nread;
163 my ($ifh, $ofh, $size) = @_;
166 my $nread = read $ifh, $buffer, ($size < 4096 ?
$size : 4096);
167 die "read(): $OS_ERROR" unless defined $nread;
168 die "unexpected end of file" unless $nread;
169 print {$ofh} $buffer;
177 my ($fh, $size) = locate_file
($filename);
178 return Deployable
::Tar
->new(%config, fh
=> $fh, size
=> $size);
179 } ## end sub get_sub_tar
182 my ($fh, $size) = locate_file
('config.pl');
183 my $config_text = full_read
($fh, $size);
184 my $config = eval 'my ' . $config_text or return;
185 return $config unless wantarray;
187 } ## end sub get_config
190 my $here_tar = get_sub_tar
('here');
191 $here_tar->extract();
193 my $root_dir = $config{inspect
} ?
'root' : '/';
194 mkpath
$root_dir unless -d
$root_dir;
197 my $root_tar = get_sub_tar
('root');
198 $root_tar->extract();
202 } ## end sub save_files
204 sub execute_deploy_programs
{
205 my @deploy_programs = @
{$config{deploy
} || []};
207 if ($config{bundle
}) { # add all executable scripts in current directory
208 print {*STDERR
} "### Auto-deploying all executables in main dir\n\n";
209 my %flag_for = map { $_ => 1 } @deploy_programs;
211 for my $item (sort readdir $dh) {
212 next if $flag_for{$item};
213 next unless ((-f
$item) || (-l
$item)) && (-x
$item);
214 $flag_for{$item} = 1;
215 push @deploy_programs, $item;
216 } ## end for my $item (sort readdir...
218 } ## end if ($config{bundle})
221 for my $deploy (@deploy_programs) {
222 $deploy = catfile
('.', $deploy)
223 unless file_name_is_absolute
($deploy);
225 print {*STDERR
} "### Skipping '$deploy', not executable\n\n";
228 print {*STDERR
} "### Executing '$deploy'...\n";
229 system {$deploy} $deploy;
230 print {*STDERR
} "\n";
231 } ## end for my $deploy (@deploy_programs)
234 } ## end sub execute_deploy_programs
238 print {*STDOUT
} <<"END_OF_USAGE" ;
241 More or less, this script is intended to be launched without parameters.
242 Anyway, you can also set the following options, which will override any
243 present configuration (except in "--show-options"):
245 * --usage | --man | --help
246 print these help lines and exit
249 print script version and exit
251 * --bundle | --all-exec | -X
252 treat all executables in the main deployment directory as scripts
255 * --cleanup | -c | --no-cleanup
256 perform / don't perform temporary directory cleanup after work done
258 * --deploy | --no-deploy
259 deploy scripts are executed by default (same as specifying '--deploy')
260 but you can prevent it.
262 * --dryrun | --dry-run
263 print final options and exit
265 * --filelist | --list | -l
266 print a list of files that are shipped in the deploy script
268 * --heretar | --here-tar | -H
269 print out the tar file that contains all the files that would be
270 extracted in the temporary directory, useful to redirect to file or
271 pipe to the tar program
273 * --inspect | -i <dirname>
274 just extract all the stuff into <dirname> for inspection. Implies
275 --no-deploy, --no-tempdir, ignores --bundle (as a consequence of
276 --no-deploy), disables --cleanup and sets the working directory
280 don’t use system "tar"
282 * --roottar | --root-tar | -R
283 print out the tar file that contains all the files that would be
284 extracted in the root directory, useful to redirect to file or
285 pipe to the tar program
287 * --show | --show-options | -s
288 print configured options and exit
290 * --tar | -t <program-path>
291 set the system "tar" program to use.
293 * --tempdir | --no-tempdir
294 by default a temporary directory is created (same as specifying
295 '--tempdir'), but you can execute directly in the workdir (see below)
298 * --workdir | --work-directory | --deploy-directory | -w
299 working base directory (a temporary subdirectory will be created
307 print "$0 version $VERSION\n";
312 package Deployable::Tar;
316 my $self = { ref $_[0] ? %{$_[0]} : @_ };
317 $package = 'Deployable::Tar::Internal';
318 if (! $self->{'no-tar'}) {
319 if ((exists $self->{tar}) || (open my $fh, '-|', 'tar', '--help')) {
320 $package = 'Deployable::Tar::External';
321 $self->{tar} ||= 'tar';
324 bless $self, $package;
325 $self->initialise() if $self->can('initialise');
329 package Deployable::Tar::External;
330 use English qw( -no_match_vars );
334 my $compression = $self->{bzip2
} ?
'j'
335 : $self->{gzip
} ?
'z'
337 $self->{_list_command
} = 'tv' . $compression . 'f';
338 $self->{_extract_command
} = 'x' . $compression . 'f';
343 open my $tfh, '|-', $self->{tar
}, $self->{_list_command
}, '-'
344 or die "open() on pipe to tar: $OS_ERROR";
345 main
::copy
($self->{fh
}, $tfh, $self->{size
});
351 open my $tfh, '|-', $self->{tar
}, $self->{_extract_command
}, '-'
352 or die "open() on pipe to tar: $OS_ERROR";
353 main
::copy
($self->{fh
}, $tfh, $self->{size
});
357 package Deployable
::Tar
::Internal
;
358 use English
qw( -no_match_vars );
363 my $data = main
::full_read
($self->{fh
}, $self->{size
});
364 open my $fh, '<', \
$data or die "open() on internal variable: $OS_ERROR";
366 require Archive
::Tar
;
367 $self->{_tar
} = Archive
::Tar
->new();
368 $self->{_tar
}->read($fh);
375 print {*STDOUT
} " $_\n" for $self->{_tar
}->list_files();
381 $self->{_tar
}->extract();