diff --git a/lib/Rex/Commands/Sync.pm b/lib/Rex/Commands/Sync.pm index 0d937dfd3..eb4714440 100644 --- a/lib/Rex/Commands/Sync.pm +++ b/lib/Rex/Commands/Sync.pm @@ -94,70 +94,54 @@ sub sync_up { $source = get_file_path( $source, caller ); # - # first, get all files on source side + # first, build excludes list # - my @local_files = _get_local_files($source); - #print Dumper(\@local_files); + my $excludes = $options->{exclude} ||= []; + $excludes = [$excludes] unless ref($excludes) eq 'ARRAY'; - # - # second, get all files from destination side - # + my @excluded_files = @{$excludes}; - my @remote_files = _get_remote_files($dest); + my $check_exclude_file = sub { + my ($file) = @_; + $file =~ s{^/}{}; - #print Dumper(\@remote_files); + for my $cmp (@excluded_files) { + return 1 if match_glob( $cmp, $file ); + } + + return 0; + }; # - # third, get the difference + # second, get all files on source side (minus excludes) # + my @local_files = _get_local_files( $source, $check_exclude_file ); - my @diff = _diff_files( \@local_files, \@remote_files ); - - #print Dumper(\@diff); + #print Dumper(\@local_files); # - # fourth, build excludes list + # third, get all files from destination side (minus excludes) # - my $excludes = $options->{exclude} ||= []; - $excludes = [$excludes] unless ref($excludes) eq 'ARRAY'; + my @remote_files = _get_remote_files( $dest, $check_exclude_file ); - my @excluded_files = @{$excludes}; + #print Dumper(\@remote_files); # - # fifth, upload the different files + # fourth, get the difference # - my $check_exclude_file = sub { - my ( $file, $cmp ) = @_; - if ( $cmp =~ m/\// ) { - - # this is a directory exclude - if ( match_glob( $cmp, $file ) || match_glob( $cmp, substr( $file, 1 ) ) ) - { - return 1; - } - - return 0; - } + my @diff = _diff_files( \@local_files, \@remote_files ); - if ( match_glob( $cmp, basename($file) ) ) { - return 1; - } + #print Dumper(\@diff); - return 0; - }; + # + # fifth, upload the different files + # my @uploaded_files; for my $file (@diff) { - next - if ( - scalar( - grep { $check_exclude_file->( $file->{name}, $_ ) } @excluded_files - ) > 0 - ); - my ($dir) = ( $file->{path} =~ m/(.*)\/[^\/]+$/ ); my ($remote_dir) = ( $file->{name} =~ m/\/(.*)\/[^\/]+$/ ); @@ -252,44 +236,53 @@ sub sync_down { $dest = resolv_path($dest); # - # first, get all files on dest side + # first, build excludes list # - my @local_files = _get_local_files($dest); - #print Dumper(\@local_files); + my $excludes = $options->{exclude} ||= []; + $excludes = [$excludes] unless ref($excludes) eq 'ARRAY'; + + my @excluded_files = @{$excludes}; + + my $check_exclude_file = sub { + my ($file) = @_; + $file =~ s{^/}{}; + + for my $cmp (@excluded_files) { + return 1 if match_glob( $cmp, $file ); + } + + return 0; + }; # - # second, get all files from source side + # second, get all files on dest side (minus excludes) # + my @local_files = _get_local_files( $dest, $check_exclude_file ); - my @remote_files = _get_remote_files($source); - - #print Dumper(\@remote_files); + #print Dumper(\@local_files); # - # third, get the difference + # third, get all files from source side (minus excludes) # - my @diff = _diff_files( \@remote_files, \@local_files ); + my @remote_files = _get_remote_files( $source, $check_exclude_file ); - #print Dumper(\@diff); + #print Dumper(\@remote_files); # - # fourth, build excludes list + # fourth, get the difference # - my $excludes = $options->{exclude} ||= []; - $excludes = [$excludes] unless ref($excludes) eq 'ARRAY'; + my @diff = _diff_files( \@remote_files, \@local_files ); - my @excluded_files = map { glob_to_regex($_); } @{$excludes}; + #print Dumper(\@diff); # # fifth, download the different files # for my $file (@diff) { - next if grep { basename( $file->{name} ) =~ $_ } @excluded_files; - my ($dir) = ( $file->{path} =~ m/(.*)\/[^\/]+$/ ); my ($remote_dir) = ( $file->{name} =~ m/\/(.*)\/[^\/]+$/ ); @@ -326,7 +319,7 @@ sub sync_down { } sub _get_local_files { - my ($source) = @_; + my ( $source, $exclude_sub ) = @_; if ( !-d $source ) { die("$source : no such directory."); } @@ -337,13 +330,15 @@ sub _get_local_files { for my $entry ( list_files($dir) ) { next if ( $entry eq "." ); next if ( $entry eq ".." ); + + my $name = "$dir/$entry"; + $name =~ s/^\Q$source\E//; + next if $exclude_sub->($name); + if ( is_dir("$dir/$entry") ) { push( @dirs, "$dir/$entry" ); next; } - - my $name = "$dir/$entry"; - $name =~ s/^\Q$source\E//; push( @local_files, { @@ -361,7 +356,7 @@ sub _get_local_files { } sub _get_remote_files { - my ($dest) = @_; + my ( $dest, $exclude_sub ) = @_; if ( !is_dir($dest) ) { die("$dest : no such directory."); } @@ -372,13 +367,16 @@ sub _get_remote_files { for my $entry ( list_files($dir) ) { next if ( $entry eq "." ); next if ( $entry eq ".." ); + + my $name = "$dir/$entry"; + $name =~ s/^\Q$dest\E//; + next if $exclude_sub->($name); + if ( is_dir("$dir/$entry") ) { push( @remote_dirs, "$dir/$entry" ); next; } - my $name = "$dir/$entry"; - $name =~ s/^\Q$dest\E//; push( @remote_files, { @@ -411,3 +409,4 @@ sub _diff_files { } 1; + diff --git a/t/sync.t b/t/sync.t new file mode 100755 index 000000000..e1f6151eb --- /dev/null +++ b/t/sync.t @@ -0,0 +1,132 @@ +#!/usr/bin/env perl + +use v5.12.5; +use warnings; + +use Test::More; + +our $VERSION = '9999.99.99_99'; # VERSION +use autodie; + +use File::Find; +use File::Temp qw(tempdir); +use Rex::Task; +use Rex::Commands::Sync; +use Test::Deep; + +subtest 'should sync_up' => sub { + my $source = 't/sync'; + my $target = prepare_directory(); + + run_task( + sub { + sync_up $source, $target; + } + ); + + compare_contents( $source, $target ); +}; + +subtest 'should sync_up with excludes' => sub { + my $source = 't/sync'; + my $target = prepare_directory(); + + # NOTE: file4 should not be excluded, as it is nested + run_task( + sub { + sync_up $source, $target, + { exclude => [ 'dir/file2', 'file4', 'dir with spaces' ] }; + } + ); + + compare_contents( $source, $target, + [ '/dir/file2', '/dir with spaces', '/dir with spaces/file3' ] ); +}; + +subtest 'should sync_down' => sub { + my $source = 't/sync'; + my $target = prepare_directory(); + + run_task( + sub { + sync_down $source, $target; + } + ); + + compare_contents( $source, $target ); +}; + +subtest 'should sync_down with excludes' => sub { + my $source = 't/sync'; + my $target = prepare_directory(); + + # NOTE: file4 should not be excluded, as it is nested + run_task( + sub { + sync_down $source, $target, + { exclude => [ 'dir/file2', 'file4', 'dir with spaces' ] }; + } + ); + + compare_contents( $source, $target, + [ '/dir/file2', '/dir with spaces', '/dir with spaces/file3' ] ); +}; + +sub prepare_directory { + my $target = tempdir( CLEANUP => 1 ); + die unless -d $target; + + return $target; +} + +sub run_task { + my ($func) = @_; + + my $task = Rex::Task->new( + name => 'sync_test', + func => $func, + ); + + $task->run(''); +} + +sub compare_contents { + my ( $source, $target, $excluded ) = @_; + $excluded //= []; + my %excluded_map = map { $_ => 1 } @{$excluded}; + + # test sync results + my ( @expected, @result ); + + # expected results + find( + { + wanted => sub { + s/$source//; + return unless length; + return if $excluded_map{$_}; + push @expected, $_; + }, + no_chdir => 1 + }, + $source + ); + + # actual results + find( + { + wanted => sub { + s/$target//; + return unless length; + push @result, $_; + }, + no_chdir => 1 + }, + $target + ); + + cmp_deeply( \@result, set(@expected), 'synced dir matches' ); +} + +done_testing; + diff --git a/t/sync/dir/file4 b/t/sync/dir/file4 new file mode 100644 index 000000000..e69de29bb