File Coverage

script/git-perl
Criterion Covered Total %
statement 172 189 91.0
branch 81 108 75.0
condition 18 30 60.0
subroutine 9 9 100.0
pod n/a
total 280 336 83.3


line stmt bran cond sub pod time code
1             #!/usr/bin/env perl
2              
3 25         2649993 our $VERSION = "0.1.18";
4              
5             # 0.1.14 - 2022-07-21 15:25
6             # - further changelogs will be listed in ChangeLog file
7             # 0.1.13 - 2022-07-21 14:12
8             # - fixed finding module listed on cli between recent and real repositories
9             # - fixed tests - checking is repository cloned and where it is clone
10             # - skip testing for Windows OS / not supported / linux only
11             # - add comments in scripts for few tests
12             # - fixed stderr leak when reading from nonexisting config file
13             # 0.1.12 - 2022.07.05
14             # - rebuild distribution to fix changes from 0.1.11, related to Kwalitee
15             # 0.1.11 - 2022.07.05
16             # - fixed Kwalitee Issues for distribution
17             # - find module name from Makefile.PL in a different way, additionally
18             # 0.1.10 - 2022.07.03
19             # - use HTTP::Tiny instead of curl
20             # 0.1.9 - 2022.07.03
21             # - added tests
22             # 0.1.8 - 2022.07.03
23             # - updated bugtracker link in Makefile.PL
24             # 0.1.7 - 2022.07.03
25             # - added CONTRIBUTING.md
26             # - fixed bugtracker in Makefile.PL
27             # 0.1.6 - 2022.07.03
28             # - updated bugtracking/issues link in Makefile.PL
29             # - added ChangeLog
30             # 0.1.5 - 2022.07.02
31             # - updated documentation in script/git-perl and README.md to use head1 (head2 looks awful)
32             # 0.1.4 - 2022.07.02
33             # - moved README into README.md, so it can be visible on github and not rendered by metacpan
34             # - testing head2 instead of head1 on metacpan/github
35             # 0.1.3 - 2022.07.02
36             # - updated AUTHOR information in README and git-perl
37             # - updated link to git-perl from App::Git::Perl
38             # - removed required strict and warnings from Makefile.PL
39             # - updated Makefile.PL to reflect minimum required Perl version of 5.6.0 (using perlver command)
40             # 0.1.2 - 2022.07.02
41             # - bumped version to 0.1.2, since I forgot to update version in lib/App/Git/Perl.pm
42             # 0.1.1 - 2022.07.02
43             # - remove "use strict", and "use warnings"
44             # - updated documentation in README and script/git-perl to show 'git-perl' and not 'git' in metacpan
45             # 0.1.0 - 2022.07.02
46             # - initial commit
47              
48 25         139 my $configfile = "$ENV{HOME}/.config/git-perl.conf";
49 25         174 my $gitdirs;
50              
51             # init
52 25         160 $gitdirs = config("dir");
53 25 50       630 $gitdirs = "." if ( not $gitdirs );
54 25 50       1109 system("mkdir -p \"$gitdirs\"") if ( not -d $gitdirs );
55              
56             sub logger {
57 51     51   761 print "LOG: @_\n";
58             }
59              
60             sub usage {
61 1     1   21 print <<"EOF";
62              
63             git-perl $VERSION
64              
65             Created to make you easier to monitor latest changes in perl modules, and make you collaborate faster.
66             Just put it somewhere in your \$PATH, and call like "git perl".
67              
68             Prepared by Nedzad Hrnjica.
69              
70             Usage:
71              
72             git perl recent = shows recent list of changes from https://metacpan.org/recent
73             git perl log BAYASHI/Object-Container-0.16 = git clone repository and show latest changes
74             git perl log BAYASHI/Object-Container-0.16 remove = remove cloned repository
75             git perl log Log::Any = git clone repository and show latest changes
76             git perl log Log::Any remove = remove cloned repository
77             git perl clone BAYASHI/Object-Container-0.16 = git clone repository
78             git perl clone BAYASHI/Object-Container-0.16 remove = remove cloned repository
79             git perl clone Log::Any = git clone repository
80             git perl clone Log::Any remove = remove cloned repository
81             git perl local = list cloned repositories
82             git perl local object-container-perl = list cloned repository 'object-container-perl'
83             git perl local object-container-perl log = show latest changes in repository
84             git perl local object-container-perl remove = remove local repository stored in 'object-container-perl'
85             git perl local Log::Any = git clone repository ( get remote repository locally )
86             git perl local Log::Any remove = remove cloned repository
87              
88             git perl config = show current config ( from ~/.config/git-perl.conf )
89             git perl config dir = show value of 'dir' from config
90             git perl config dir ~/git/perl = set value of 'dir' to '~/git/perl'
91             git perl config --unset dir = remove variable 'dir' from config file
92              
93             EOF
94              
95             }
96              
97             sub httpget {
98 25     25   183396 use HTTP::Tiny;
  25         1802143  
  25         2367315  
99              
100 11     11   79 my $url = shift;
101 11 50       113 return "" if not $url;
102              
103 11         433 my $response = HTTP::Tiny->new->get( $url );
104              
105 11 50       6210081 if ( length $response->{content} ) {
106             return $response->{content}
107 11         30133 }
108              
109 0         0 return "";
110             }
111              
112             sub config {
113 31     31   151 my ($name,$value) = (shift,shift);
114              
115 31 100       166 if ( not $name ) {
116             # git perl config
117             # show all values from ~/.config/git-perl.conf
118 1         8720 my $output = qx{ cat "$configfile" 2>/dev/null };
119 1         29 chomp($output);
120 1         29 return $output;
121             }
122              
123 30 100       123 if ( not $value ) {
124             # git perl config 'something'
125             # show value for 'something' from ~/.config/git-perl.conf
126 28         340676 my $value = qx{ cat "$configfile" 2>/dev/null | grep "^$name=" | cut -d"=" -f2- };
127 28         692 chomp($value);
128 28         1066 return $value;
129             }
130              
131             # unset ?
132 2         8 my $unset = 0;
133 2 100       16 if ( $name eq "--unset" ) {
134 1         4 $unset = 1;
135 1         8 $name = $value;
136             }
137              
138             # If $name and $value
139              
140 2 50 33     44 if ( $name and $value ) {
141             # set value
142 2         13901 qx{ mkdir -p "$ENV{HOME}/.config/" };
143 2         23575 qx{ cat "$configfile" 2>/dev/null | grep -v "^$name=" > "$configfile" };
144 2 100       5186 qx{ echo "$name=$value" >> "$configfile" } if ( not $unset );
145              
146             # If final file is empty, remove it from disk.
147 2 100       165 if ( -z "$configfile" ) {
148 1         145 unlink "$configfile";
149             }
150              
151 2         85 return "";
152             }
153              
154             }
155              
156             sub clone {
157 10     10   52 my $module = shift;
158 10 50       87 return if ( not $module );
159              
160 10 100       209 if ( $module =~ /::/ ) {
161             # That's ok, we will keep this for query.
162             # Example: App::Git::Perl
163             } else {
164              
165 4         77 $module =~ s#.*/##; # keep only last part without any '/'
166 4         30 $module =~ s/-[0-9].*//; # remove any part after '-number' from distribution
167 4         34 $module =~ s/-/::/g; # convert '-' into '::'
168              
169             # NHRNJICA/App-Git-Perl-0.1.12
170             # App-Git-Perl-0.1.12
171             # App-Git-Perl
172             # App::Git::Perl
173             }
174              
175             # https://fastapi.metacpan.org/release/_search?sort=date:desc&q=main_module:%22App::Git::Perl%22%20AND%20status:latest&_source=main_module,resources.repository.url
176              
177 10         118 my $url = join("", 'https://fastapi.metacpan.org/release/_search?sort=date:desc&q=main_module:%22',$module,'%22%20AND%20status:latest&_source=main_module,resources.repository.url');
178              
179 10         230 logger("Getting repository details from '$url'...");
180              
181 10         138 my $content = httpget( $url );
182              
183 10         72 my $repository = "";
184 10 100       773 if ( $content =~ /"resources".* "repository".* "url" : "([^"]*)"/s ) {
185 8         539 $repository = $1;
186             }
187              
188 10         106 logger("REPOSITORY: [$repository]");
189              
190 10 100       535 if ( not $repository ) {
191 2         30 logger("ERROR: Respository for module '$module' does not exist!");
192 2         10 return;
193             }
194              
195 8         697 logger("Cloning remote repository '$repository'...");
196 8         85116 qx{ mkdir -p "${gitdirs}" };
197 8         4761857 my $clonetext = qx{ cd ${gitdirs}; git clone "$repository" 2>&1 };
198 8         429 my ($subdir) = $clonetext =~ /Cloning into '(.*)'/;
199 8         134 chomp($clonetext);
200 8         532 logger("CLONED: [$clonetext]");
201 8 50 66     479 $subdir = "" if ( $subdir and not -d "$gitdirs/$subdir" );
202 8 100       200 if ( $subdir ) {
203 6         143 logger("Cloned into: [$subdir]");
204             } else {
205 2         41 ($subdir) = $clonetext =~ /destination path '(.*)' already exists/;
206 2 50       67 logger("Cloned repository already exists in: [$subdir]") if ( $subdir );
207             }
208              
209 8         236 return $subdir;
210             }
211              
212             sub remove {
213 6     6   31 my $subdir = shift;
214 6 50       67 return if ( not $subdir );
215 6 50       227 if ( not -d "$gitdirs/$subdir") {
216 0         0 logger("ERROR: Subdir '$subdir' does not exist!");
217 0         0 return;
218             }
219              
220             # $subdir should be OK, when getting it from GIT. But ...
221             # make sure that $subdir is not dangerous
222 6         87 $subdir =~ s#/$##; # remove last '/' if this is direct reference to subdir
223 6         45 $subdir =~ s#.*/##; # remove any path element from $subdir
224 6 50       59 $subdir = "" if ( $subdir eq "." ); # prevent removing current dir
225 6 50       53 $subdir = "" if ( $subdir eq ".." ); # prevent removing parent dir
226 6         12 my $removed = 0;
227 6 50 33     190 if ( $subdir and -d "$gitdirs/$subdir" ) {
228 6         106247 system( "rm -fr \"$gitdirs/$subdir\"" ); # DANGEROUS !?
229 6         436 $removed = 1;
230             }
231 6 50 33     603 if ( $removed and ! -d "$gitdirs/$subdir" ) {
232 6         261 print "Removed repository stored in subdir '$subdir'.\n";
233             } else {
234 0         0 print "ERROR: Unable to remove repository subdir '$subdir'!\n";
235             }
236             }
237              
238             sub gitlog {
239 6     6   31 my $module = shift;
240 6 50       57 return if ( not $module );
241 6         61 my $subcommand = shift;
242              
243 6         53 my $subdir = "";
244              
245             # If we want to see log from already cloned subdirectory (as output from 'git perl local')
246 6 100       275 if ( -d "$gitdirs/$module" ) {
247 2         20 $subdir = $module;
248             }
249              
250             # If that is not local subdir, get it from repository
251 6 100       76 if ( not $subdir ) {
252             # Clone repostiory locally, or get '$subdir' where it is cloned already
253 4         61 $subdir = clone($module);
254 4 100       50 return if ( not $subdir );
255             }
256              
257             # If user ask local repository to be removed, do so.
258 5         54 my $removed = 0;
259 5 100 66     193 if ( $subcommand and $subcommand eq "remove" ) {
260 2         51 logger("About to remove subdir for module '$module'...");
261 2         34 remove( $subdir );
262 2         32 return;
263             }
264              
265             # Get lastlog and show all changes since last tag
266             # If no tags used, show all changes since epoch
267 3         38028 my $lasttag = qx{ cd "$gitdirs/$subdir"; git tag -l | tail -n 2 | head -n 1 };
268 3         91 chomp($lasttag);
269 3         166 logger("Last tag: [$lasttag]");
270 3 50       89 if ( $lasttag ) {
271 3         80550 system("cd \"$gitdirs/$subdir\"; git log -p ${lasttag}..HEAD");
272             } else {
273 0         0 system("cd \"$gitdirs/$subdir\"; git log -p");
274             }
275              
276             # Repository will stay locally, so inform user.
277 3         265 print "Cloned into: $subdir\n";
278             }
279              
280             sub main {
281 25     25   326 my $command = shift;
282              
283 25 100       374 if ( not $command ) {
284 1         15 usage();
285 1         0 exit;
286             }
287              
288 24 100       327 if ( $command eq "recent" ) {
289              
290             # get a list of recent repositories
291 1         18 my $content = httpget("https://fastapi.metacpan.org/release/_search?_source=name,author,date&sort=date:desc&size=100");
292              
293 1         6 my @repositories;
294 1         808 my @records = split(/^ *{/m, $content);
295 1         13 foreach my $record (@records) {
296 102 100       1293 if ( $record =~ /"author" : "([^"]*)".* "date" : "([^"]*)".* "name" : "([^"]*)"/s ) {
297 100         168 my $author = $1;
298 100         149 my $date = $2;
299 100         162 my $name = $3;
300 100         283 $date =~ s/T/ /g;
301 100         260 push( @repositories, "$date $author/$name" );
302             }
303             }
304 1         2 @repositories = reverse @repositories;
305 1         27 print join("\n", @repositories, "");
306              
307 1         0 exit;
308              
309             }
310              
311 23 100       309 if ( $command eq "clone" ) {
312 5         58 my $module = shift;
313 5 50       95 exit if ( not $module );
314              
315             # If user asked to remove subdirectory directly, do it immediately
316              
317 5         39 my ($subcommand) = shift;
318 5 50 66     240 if ( $subcommand and $subcommand eq "remove" and -d "$module" ) {
      66        
319 2         24 remove( $module );
320 2         0 exit;
321             }
322              
323             # Clone repository locally, or get '$subdir' where it is cloned already
324              
325 3         57 my $subdir = clone($module);
326 3 50       66 return if ( not $subdir );
327              
328 3         30 print "Cloned into: $subdir\n";
329              
330 3 50 33     69 if ( $subcommand and $subcommand eq "remove" ) {
331 0         0 remove( $subdir );
332 0         0 exit;
333             }
334              
335 3         0 exit;
336             }
337              
338 18 100       258 if ( $command eq "log" ) {
339 5         86 my $module = shift;
340 5 50       57 exit if ( not $module );
341              
342             # Call gitlog for $module, and add additional parameter if asked by user (e.g. 'remove')
343 5         84 gitlog($module,shift);
344              
345 5         0 exit;
346             }
347              
348 13 100       164 if ( $command eq "local" ) {
349 7         80 my ($subdir, $subcommand) = (shift,shift);
350 7 100       102 $subdir =~ s#/$## if ( $subdir ); # remove trailing '/' if provided as $subdir
351 7 100 100     286 if ( $subdir and not -d "$gitdirs/$subdir" ) {
352 3         68 my $newsubdir = clone( $subdir ); # it suppose the '$subdir' is actually '$modulename'
353 3 100       44 $subdir = $newsubdir if ( $newsubdir ); # if cloned, use it
354             }
355 7 100 100     178 if ( $subdir and $subcommand ) {
356 3 100       53 if ( $subcommand eq "remove" ) {
357 2         30 remove( $subdir );
358 2         0 exit;
359             }
360 1 50       9 if ( $subcommand eq "log" ) {
361 1         13 gitlog( $subdir );
362 1         0 exit;
363             }
364             }
365 4         61001 my @local = qx{ cd "${gitdirs}"; ls -1 */dist.ini */Makefile.PL 2>/dev/null | cut -d"/" -f1 | sort | uniq };
366 4         97 chomp( @local );
367 4         71 foreach my $local (@local) {
368             # If user provided subdir, return data only for that module
369 4 100       92 if ( $subdir ) {
370 3 100       423 next if ( $local !~ /$subdir/ );
371             }
372 3         41 my $modulefile = "";
373 3         15 my $module = "";
374 3         19 my $VERSION = "";
375              
376             # Makefile.PL
377 3         54262 $modulefile = qx{ cd "${gitdirs}"; grep VERSION_FROM "$local/Makefile.PL" 2>/dev/null | grep "=>" | cut -d"=" -f2 | cut -d\\' -f2 | cut -d\\" -f2 | sed -e "s#^#$local/#" };
378 3         91 chomp( $modulefile );
379 3 50       72 if ( not $modulefile ) {
380 0         0 $modulefile = qx{ cd "${gitdirs}"; grep "all_from" "$local/Makefile.PL" 2>/dev/null | cut -d\\' -f2 | cut -d\\" -f2 | sed -e "s#^#$local/#" };
381 0         0 chomp( $modulefile );
382             }
383 3 50       59 if ( not $modulefile ) {
384 0         0 $modulefile = qx{ cd "${gitdirs}"; find "$local/lib" -iname "*.pm" 2>/dev/null | xargs grep -H "package " | sed -e "s/.pm:.*/.pm/" | head -n 1 };
385 0         0 chomp( $modulefile );
386             }
387 3 50       76 if ( $modulefile ) {
388 3         50020 $module = qx{ cd "${gitdirs}"; cat "$modulefile" | grep "^\\s*package " | sed -e "s/package //" | cut -d";" -f1 | head -n 1 };
389 3         87 chomp( $module );
390 3 50       101 if ( not $module ) {
391 3         64 $module = $modulefile;
392             }
393 3         52873 $VERSION = qx{ cd "${gitdirs}"; cat "$modulefile" | grep VERSION | grep "[0-9]" | cut -d\\' -f2 | cut -d\\" -f2 | head -n 1 };
394 3         107 chomp( $VERSION );
395             } else {
396             # This is just guessing. It returns our...VERSION from first found file.
397 0         0 $module = qx{ cd "${gitdirs}"; cd "$local/" ; grep -rsn VERSION * | grep our | cut -d":" -f1 };
398 0         0 chomp( $module );
399 0         0 $VERSION = qx{ cd "${gitdirs}"; cd "$local/" ; grep -rsn VERSION * | grep our | cut -d\\' -f2 | cut -d\\" -f2 | head -n 1 };
400 0         0 chomp( $VERSION );
401             }
402              
403             # $module/$VERSION is just provisioning data, not really used anywhere
404 3         112 print "$local $module $VERSION\n";
405             }
406 4         0 exit;
407             }
408              
409 6 50       48 if ( $command eq "config" ) {
410 6         50 my ($name, $value) = (shift,shift);
411              
412 6         92 my $output = config($name,$value);
413 6 100       150 print "$output\n" if ($output);
414 6           exit;
415             }
416              
417 0           print "ERROR: I do now know what you want? See usage:\n";
418 0           usage();
419              
420             }
421              
422 25         614 main(@ARGV);
423              
424             __END__ # Documentation