File Coverage

blib/lib/Dist/Zilla/Plugin/Git/GatherDir.pm
Criterion Covered Total %
statement 15 19 78.9
branch 0 6 0.0
condition 0 3 0.0
subroutine 5 6 83.3
pod n/a
total 20 34 58.8


line stmt bran cond sub pod time code
1             #
2             # This file is part of Dist-Zilla-Plugin-Git
3             #
4             # This software is copyright (c) 2009 by Jerome Quelin.
5             #
6             # This is free software; you can redistribute it and/or modify it under
7             # the same terms as the Perl 5 programming language system itself.
8             #
9             package Dist::Zilla::Plugin::Git::GatherDir;
10             # ABSTRACT: Gather all tracked files in a Git working directory
11              
12             our $VERSION = '2.048';
13              
14 2     2   2494410 use Moose;
  2         6  
  2         18  
15             extends 'Dist::Zilla::Plugin::GatherDir' => { -version => 4.200016 }; # exclude_match
16              
17             #pod =head1 SYNOPSIS
18             #pod
19             #pod In your F<dist.ini>:
20             #pod
21             #pod [Git::GatherDir]
22             #pod root = . ; this is the default
23             #pod prefix = ; this is the default
24             #pod include_dotfiles = 0 ; this is the default
25             #pod include_untracked = 0 ; this is the default
26             #pod exclude_filename = dir/skip ; there is no default
27             #pod exclude_match = ^local_ ; there is no default
28             #pod
29             #pod =head1 DESCRIPTION
30             #pod
31             #pod This is a trivial variant of the L<GatherDir|Dist::Zilla::Plugin::GatherDir>
32             #pod plugin. It looks in the directory named in the L</root> attribute and adds all
33             #pod the Git tracked files it finds there (as determined by C<git ls-files>). If the
34             #pod root begins with a tilde, the tilde portion is passed through C<glob()> first.
35             #pod
36             #pod Most users just need:
37             #pod
38             #pod [Git::GatherDir]
39             #pod
40             #pod ...and this will pick up all tracked files from the current directory into the
41             #pod dist. You can use it multiple times, as you can any other plugin, by providing
42             #pod a plugin name. For example, if you want to include external specification
43             #pod files into a subdir of your dist, you might write:
44             #pod
45             #pod [Git::GatherDir]
46             #pod ; this plugin needs no config and gathers most of your files
47             #pod
48             #pod [Git::GatherDir / SpecFiles]
49             #pod ; this plugin gets all tracked files in the root dir and adds them under ./spec
50             #pod root = ~/projects/my-project/spec
51             #pod prefix = spec
52             #pod
53             #pod =cut
54              
55 2     2   12474 use List::Util 1.45 qw(uniq);
  2         56  
  2         165  
56 2     2   1331 use Types::Standard 'Bool';
  2         146339  
  2         20  
57              
58 2     2   1503 use namespace::autoclean;
  2         5  
  2         23  
59              
60             #pod =attr root
61             #pod
62             #pod This is the directory in which to look for files. If not given, it defaults to
63             #pod the dist root -- generally, the place where your F<dist.ini> or other
64             #pod configuration file is located. It may begin with C<~> (or C<~user>)
65             #pod to mean your (or some other user's) home directory. If a relative path,
66             #pod it's relative to the dist root. It does not need to be the root of a
67             #pod Git repository, but it must be inside a repository.
68             #pod
69             #pod =attr prefix
70             #pod
71             #pod This parameter can be set to gather all the files found under a common
72             #pod directory. See the L<description|DESCRIPTION> above for an example.
73             #pod
74             #pod =attr include_dotfiles
75             #pod
76             #pod By default, files will not be included if they begin with a dot. This goes
77             #pod both for files and for directories relative to the C<root>.
78             #pod
79             #pod In almost all cases, the default value (false) is correct.
80             #pod
81             #pod =attr include_untracked
82             #pod
83             #pod By default, files not tracked by Git will not be gathered. If this is
84             #pod set to a true value, then untracked files not covered by a Git ignore
85             #pod pattern (i.e. those reported by C<git ls-files -o --exclude-standard>)
86             #pod are also gathered (and you'll probably want to use
87             #pod L<Git::Check|Dist::Zilla::Plugin::Git::Check> to ensure all files are
88             #pod checked in before a release).
89             #pod
90             #pod C<include_untracked> requires at least Git 1.5.4, but you should
91             #pod probably not use it if your Git is older than 1.6.5.2. Versions
92             #pod before that would not list files matched by your F<.gitignore>, even
93             #pod if they were already being tracked by Git (which means they will not
94             #pod be gathered, even though they should be). Whether that is a problem
95             #pod depends on the contents of your exclude files (including the global
96             #pod one, if any).
97             #pod
98             #pod =attr follow_symlinks
99             #pod
100             #pod Git::GatherDir does not honor GatherDir's
101             #pod L<follow_symlinks|Dist::Zilla::Plugin::GatherDir/follow_symlinks>
102             #pod option. While the attribute exists (because Git::GatherDir is a
103             #pod subclass), setting it has no effect.
104             #pod
105             #pod Directories that are symlinks will not be gathered. Instead, you'll
106             #pod get a message saying C<WARNING: %s is symlink to directory, skipping it>.
107             #pod To suppress the warning, add that directory to C<exclude_filename> or
108             #pod C<exclude_match>. To gather the files in the symlinked directory, use
109             #pod a second instance of GatherDir or Git::GatherDir with appropriate
110             #pod C<root> and C<prefix> options.
111             #pod
112             #pod Files which are symlinks are always gathered.
113             #pod
114             #pod =attr exclude_filename
115             #pod
116             #pod To exclude certain files from being gathered, use the C<exclude_filename>
117             #pod option. This may be used multiple times to specify multiple files to exclude.
118             #pod
119             #pod =attr exclude_match
120             #pod
121             #pod This is just like C<exclude_filename> but provides a regular expression
122             #pod pattern. Files matching the pattern are not gathered. This may be used
123             #pod multiple times to specify multiple patterns to exclude.
124             #pod
125             #pod =cut
126              
127             has include_untracked => (
128             is => 'ro',
129             isa => Bool,
130             default => 0,
131             );
132              
133             around dump_config => sub
134             {
135             my $orig = shift;
136             my $self = shift;
137              
138             my $config = $self->$orig;
139              
140             $config->{+__PACKAGE__} = {
141             include_untracked => $self->include_untracked ? 1 : 0,
142             blessed($self) ne __PACKAGE__ ? ( version => $VERSION ) : (),
143             };
144              
145             foreach my $opt (qw(prune_directory follow_symlinks)) {
146             $self->log('WARNING: unused config variable "'.$opt.'"') if exists $config->{+__PACKAGE__}{$opt};
147             delete $config->{+__PACKAGE__}{$opt};
148             }
149              
150             return $config;
151             };
152              
153             override gather_files => sub {
154             my ($self) = @_;
155              
156             require Git::Wrapper;
157             require Path::Tiny;
158              
159             my $root = '' . $self->root;
160              
161             # Convert ~ portion to real directory:
162             $root =~ s{^(~[^\\/]*)([\\/])}{$self->_homedir($1) . $2}e;
163              
164             $root = Path::Tiny::path($root)->absolute($self->zilla->root);
165              
166             # Prepare to gather files
167             my $git = Git::Wrapper->new($root->stringify);
168              
169             my @opts;
170             @opts = qw(--cached --others --exclude-standard) if $self->include_untracked;
171              
172             my $exclude_regex = qr/\000/;
173             $exclude_regex = qr/$exclude_regex|$_/
174             for (@{ $self->exclude_match });
175              
176             my %is_excluded = map +($_ => 1), @{ $self->exclude_filename };
177              
178             my $prefix = $self->prefix;
179              
180             # Loop over files reported by git ls-files
181             for my $filename (uniq $git->ls_files(@opts)) {
182             # $file is a Path::Tiny relative to $root
183             my $file = Path::Tiny::path($filename);
184              
185             $self->log_debug("considering $file");
186              
187             # Exclusion tests
188             unless ($self->include_dotfiles) {
189             next if grep /^\./, split q{/}, $file->stringify;
190             }
191              
192             next if $file =~ $exclude_regex;
193             next if $is_excluded{ $file };
194              
195             # DZil can't gather directory symlinks
196             my $path = $root->child($file);
197              
198             if (-d $path) {
199             $self->log("WARNING: $file is symlink to directory, skipping it");
200             next;
201             }
202              
203             # Gather the file
204             my $fileobj = $self->_file_from_filename($path->stringify);
205              
206             $file = Path::Tiny::path($prefix, $file) if length $prefix;
207              
208             $fileobj->name($file->stringify);
209             $self->add_file($fileobj);
210             $self->log_debug("gathered $file");
211             }
212              
213             return;
214             };
215              
216             sub _homedir {
217 0     0     my ($self, $tilde_dir) = @_;
218              
219             # other architectures have no issue with globbing any kind of ~user path
220 0 0         return (glob($tilde_dir))[0] if $^O ne 'Win32';
221              
222             # on Win32, we can use the environment variable(s) for the current user
223 0 0 0       return "$]" < '5.016' ? $ENV{HOME} || $ENV{USERPROFILE} : (glob('~'))[0]
    0          
224             if $tilde_dir eq '~';
225              
226             # but otherwise, we can't support this at all, on any version.
227 0           $self->log_fatal('expanding "' . $tilde_dir . '" on Win32 not supported');
228             }
229              
230             __PACKAGE__->meta->make_immutable;
231 2     2   1551 no Moose;
  2         6  
  2         19  
232             1;
233              
234             __END__
235              
236             =pod
237              
238             =encoding UTF-8
239              
240             =head1 NAME
241              
242             Dist::Zilla::Plugin::Git::GatherDir - Gather all tracked files in a Git working directory
243              
244             =head1 VERSION
245              
246             version 2.048
247              
248             =head1 SYNOPSIS
249              
250             In your F<dist.ini>:
251              
252             [Git::GatherDir]
253             root = . ; this is the default
254             prefix = ; this is the default
255             include_dotfiles = 0 ; this is the default
256             include_untracked = 0 ; this is the default
257             exclude_filename = dir/skip ; there is no default
258             exclude_match = ^local_ ; there is no default
259              
260             =head1 DESCRIPTION
261              
262             This is a trivial variant of the L<GatherDir|Dist::Zilla::Plugin::GatherDir>
263             plugin. It looks in the directory named in the L</root> attribute and adds all
264             the Git tracked files it finds there (as determined by C<git ls-files>). If the
265             root begins with a tilde, the tilde portion is passed through C<glob()> first.
266              
267             Most users just need:
268              
269             [Git::GatherDir]
270              
271             ...and this will pick up all tracked files from the current directory into the
272             dist. You can use it multiple times, as you can any other plugin, by providing
273             a plugin name. For example, if you want to include external specification
274             files into a subdir of your dist, you might write:
275              
276             [Git::GatherDir]
277             ; this plugin needs no config and gathers most of your files
278              
279             [Git::GatherDir / SpecFiles]
280             ; this plugin gets all tracked files in the root dir and adds them under ./spec
281             root = ~/projects/my-project/spec
282             prefix = spec
283              
284             =head1 ATTRIBUTES
285              
286             =head2 root
287              
288             This is the directory in which to look for files. If not given, it defaults to
289             the dist root -- generally, the place where your F<dist.ini> or other
290             configuration file is located. It may begin with C<~> (or C<~user>)
291             to mean your (or some other user's) home directory. If a relative path,
292             it's relative to the dist root. It does not need to be the root of a
293             Git repository, but it must be inside a repository.
294              
295             =head2 prefix
296              
297             This parameter can be set to gather all the files found under a common
298             directory. See the L<description|DESCRIPTION> above for an example.
299              
300             =head2 include_dotfiles
301              
302             By default, files will not be included if they begin with a dot. This goes
303             both for files and for directories relative to the C<root>.
304              
305             In almost all cases, the default value (false) is correct.
306              
307             =head2 include_untracked
308              
309             By default, files not tracked by Git will not be gathered. If this is
310             set to a true value, then untracked files not covered by a Git ignore
311             pattern (i.e. those reported by C<git ls-files -o --exclude-standard>)
312             are also gathered (and you'll probably want to use
313             L<Git::Check|Dist::Zilla::Plugin::Git::Check> to ensure all files are
314             checked in before a release).
315              
316             C<include_untracked> requires at least Git 1.5.4, but you should
317             probably not use it if your Git is older than 1.6.5.2. Versions
318             before that would not list files matched by your F<.gitignore>, even
319             if they were already being tracked by Git (which means they will not
320             be gathered, even though they should be). Whether that is a problem
321             depends on the contents of your exclude files (including the global
322             one, if any).
323              
324             =head2 follow_symlinks
325              
326             Git::GatherDir does not honor GatherDir's
327             L<follow_symlinks|Dist::Zilla::Plugin::GatherDir/follow_symlinks>
328             option. While the attribute exists (because Git::GatherDir is a
329             subclass), setting it has no effect.
330              
331             Directories that are symlinks will not be gathered. Instead, you'll
332             get a message saying C<WARNING: %s is symlink to directory, skipping it>.
333             To suppress the warning, add that directory to C<exclude_filename> or
334             C<exclude_match>. To gather the files in the symlinked directory, use
335             a second instance of GatherDir or Git::GatherDir with appropriate
336             C<root> and C<prefix> options.
337              
338             Files which are symlinks are always gathered.
339              
340             =head2 exclude_filename
341              
342             To exclude certain files from being gathered, use the C<exclude_filename>
343             option. This may be used multiple times to specify multiple files to exclude.
344              
345             =head2 exclude_match
346              
347             This is just like C<exclude_filename> but provides a regular expression
348             pattern. Files matching the pattern are not gathered. This may be used
349             multiple times to specify multiple patterns to exclude.
350              
351             =for Pod::Coverage gather_dir
352             gather_files
353              
354             =head1 SUPPORT
355              
356             Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=Dist-Zilla-Plugin-Git>
357             (or L<bug-Dist-Zilla-Plugin-Git@rt.cpan.org|mailto:bug-Dist-Zilla-Plugin-Git@rt.cpan.org>).
358              
359             There is also a mailing list available for users of this distribution, at
360             L<http://dzil.org/#mailing-list>.
361              
362             There is also an irc channel available for users of this distribution, at
363             L<C<#distzilla> on C<irc.perl.org>|irc://irc.perl.org/#distzilla>.
364              
365             =head1 AUTHOR
366              
367             Jerome Quelin
368              
369             =head1 COPYRIGHT AND LICENCE
370              
371             This software is copyright (c) 2009 by Jerome Quelin.
372              
373             This is free software; you can redistribute it and/or modify it under
374             the same terms as the Perl 5 programming language system itself.
375              
376             =cut