source: trunk/project-meta/project-meta @ 191

Last change on this file since 191 was 191, checked in by g7moreau, 6 years ago
  • Protect file and folder with simple quote
  • Property svn:executable set to *
File size: 13.1 KB
Line 
1#!/usr/bin/env perl
2#
3# 2018/01/17 Gabriel Moreau <Gabriel.Moreau(A)univ-grenoble-alpes.fr>
4#
5# apt-get install yamllint libyaml-syck-perl libtemplate-perl libarchive-zip-perl
6
7use strict;
8use warnings;
9
10use File::Copy qw{copy};   
11use YAML::Syck;
12use Getopt::Long();
13use Cwd();
14use Template;
15use Archive::Zip qw( :ERROR_CODES :CONSTANTS );
16
17
18my ($verbose);
19Getopt::Long::GetOptions(
20   'verbose' => \$verbose,
21   );
22
23
24my %CMD_DB = (
25   'help'            => \&cmd_help,
26   'version'         => \&cmd_version,
27   'check'           => \&cmd_check,
28   'make-link'       => \&cmd_make_link,
29   'make-zip'        => \&cmd_make_zip,
30   'make-author'     => \&cmd_make_author,
31   'make-licence'    => \&cmd_make_licence,
32   'make-copyright'  => \&cmd_make_copyright,
33   'list-licence'    => \&cmd_list_licence,
34   );
35
36################################################################
37# main program
38################################################################
39
40my $cmd = shift @ARGV || 'help';
41if (defined $CMD_DB{$cmd}) {
42   $CMD_DB{$cmd}->(@ARGV);
43   }
44else {
45   print {*STDERR} "project-meta: command $cmd not found\n\n";
46   $CMD_DB{'help'}->();
47   exit 1;
48   }
49
50exit;
51
52################################################################
53# subroutine
54################################################################
55
56sub print_ok {
57   my ($key, $test) = @_;
58   
59   printf "%-35s : %s\n", $key, $test ? 'yes' : 'no';
60   }
61
62################################################################
63
64sub addfolder2list {
65   my ($folderdb, $folder) = @_;
66   
67   $folder =~ s{/[^/]+$}{};
68   
69   return if $folder !~ m{/};
70
71   $folderdb->{$folder}++;
72   return addfolder2list($folderdb, $folder);
73   }
74
75################################################################
76# command
77################################################################
78
79sub cmd_help {
80   print <<'END';
81project-meta - opendata project metafile manager
82
83 project-meta help
84 project-meta version
85 project-meta check
86 project-meta make-link
87 project-meta make-zip
88 project-meta make-author
89 project-meta make-licence
90 project-meta make-copyright
91 project-meta make-licence
92 project-meta list-licence
93END
94   }
95
96################################################################
97
98sub cmd_version {
99   print "0.0.2\n";
100   }
101
102################################################################
103
104sub cmd_check {
105   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
106
107   my $acronym     = $meta->{'project'}{'acronym'};
108   my $current_dir = Cwd::getcwd();
109   my $dap_folder  = $meta->{'public-dap'}{'dap-folder'};
110
111   print_ok 'project/acronym',                  $acronym =~ m{\d\d\w[\w\d_]+};
112   print_ok 'public-dap/dap-folder',            $dap_folder ne '' and $dap_folder =~ m{^/};
113   print_ok 'dap-folder not match current_dir', $dap_folder !~ m{$current_dir};
114
115   #print YAML::Syck::Dump($meta);
116   }
117
118################################################################
119
120sub cmd_make_link {
121   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
122   my $current_dir = Cwd::getcwd();
123   my $acronym     = $meta->{'project'}{'acronym'};
124   my $dap_folder  = $meta->{'public-dap'}{'dap-folder'};
125   my $data_set    = $meta->{'public-dap'}{'data-set'};
126
127   push @{$data_set}, 'AUTHORS.txt', 'COPYRIGHT.txt', 'LICENCE.txt';
128   {
129      # Remove doublon
130      my %seen = ();
131      @{$data_set} = grep { ! $seen{$_}++ } @{$data_set};
132      }
133
134   # Create a list of the folder
135   my %folders;
136   for my $dataset (@{$data_set}) {
137      addfolder2list(\%folders, $dataset);
138      }
139
140   print "chmod o+rX,o-w '$current_dir'\n";
141   print "mkdir -p '$dap_folder/$acronym'\n" if not -d "$dap_folder/$acronym";
142   for my $folder (sort keys %folders) {
143      print "chmod o+rX,o-w '$current_dir/$folder'\n";
144      print "mkdir -p '$dap_folder/$acronym/$folder'\n" if -d "$current_dir/$folder";
145      }
146
147   for my $dataset (@{$data_set}) {
148      if ($dataset =~ m{/}) {
149         # Folder case
150         my $folder = $dataset =~ s{/[^/]+$}{}r;
151         print "ln --symbolic --target-directory '$dap_folder/$acronym/$folder/' '$current_dir/$dataset'\n";
152         }
153      else {
154         # File case
155         print "ln --symbolic --target-directory '$dap_folder/$acronym/' '$current_dir/$dataset'\n";
156         }
157
158      }
159   print "chmod -R o+rX,o-w '$dap_folder/$acronym/'\n";
160   }
161
162################################################################
163
164sub cmd_make_zip {
165   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
166   my $current_dir = Cwd::getcwd();
167   my $data_set    = $meta->{'public-dap'}{'data-set'};
168   my $acronym     = $meta->{'project'}{'acronym'};
169
170   push @{$data_set}, 'AUTHORS.txt', 'COPYRIGHT.txt', 'LICENCE.txt';
171   {
172      # Remove doublon
173      my %seen = ();
174      @{$data_set} = grep { ! $seen{$_}++ } @{$data_set};
175      }
176
177   # Create a Zip file
178   my $zip = Archive::Zip->new();
179
180   for my $dataset (@{$data_set}) {
181      if (-d $dataset) {
182         # Folder case
183         $zip->addTree($dataset, "$acronym/$dataset");
184         }
185      elsif (-f $dataset) {
186         # File case
187         $zip->addFile($dataset, "$acronym/$dataset");
188         }
189      else {
190         # Strange case
191         print "Error: entry $dataset doesn't exists\n";
192         }
193      }
194
195   my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime time;
196   $year += 1900;
197   $mon++;
198   my $date = sprintf '%04i%02i%02i-%02i%02i', $year, $mon, $mday, $hour, $min;
199
200   # Save the Zip file
201   unless ($zip->writeToFileNamed("$current_dir/$acronym--$date.zip") == AZ_OK) {
202      die 'Error: zip write error';
203      }
204   }
205
206################################################################
207
208sub cmd_make_author {
209   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
210
211   my $current_dir = Cwd::getcwd();
212
213   my $acronym    = $meta->{'project'}{'acronym'};
214   my $authors_list = $meta->{'project'}{'authors'};
215
216   if (-f "$current_dir/AUTHORS.txt") {
217      # Test for manual or automatically generated file
218      # Automatically generated file by project-meta
219      my $automatic;
220      open my $fh, '<', "$current_dir/AUTHORS.txt" or die $!;
221      for my $line (<$fh>) {
222         $line =~ m/Automatically generated .* project-meta/i and $automatic++;
223         }
224      close $fh;
225
226      if (not $automatic) {
227         print "Warning: AUTHORS.txt already exists\n";
228         return;
229         }
230
231      print "Warning: update AUTHORS.txt\n";
232      }
233
234   my $tt = Template->new(INCLUDE_PATH => '/usr/share/project-meta/template.d');
235   my $msg_format = '';
236   $tt->process('AUTHORS.tt',
237      {
238         acronym    => $acronym,
239         authorlist => $authors_list,
240      }, \$msg_format) || die $tt->error;
241
242   open my $fh,  '>', "$current_dir/AUTHORS.txt" or die $!;
243   print $fh "$msg_format\n\n";
244   close $fh;
245   }
246
247################################################################
248
249sub cmd_make_licence {
250   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
251
252   my $current_dir = Cwd::getcwd();
253
254   if (-f "$current_dir/LICENCE.txt") {
255      print "Warning: LICENCE.txt already exists\n";
256      return;
257      }
258
259   my $licence = $meta->{'public-dap'}{'data-licence'};
260
261   if (not -f "/usr/share/project-meta/licence.d/$licence.txt") {
262      print "Error: licence $licence doesn't exists in project-meta database\n";
263      exit 1;
264      }
265
266   copy("/usr/share/project-meta/licence.d/$licence.txt", "$current_dir/LICENCE.txt")
267      or die "Error: licence copy failed - $!";
268
269   print "Info: LICENCE.txt file create\n";
270   return;
271   }
272
273################################################################
274
275sub cmd_make_copyright {
276   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
277
278   my $current_dir = Cwd::getcwd();
279
280   if (-f "$current_dir/COPYRIGHT.txt") {
281      # Test for manual or automatically generated file
282      # Automatically generated file by project-meta
283      my $automatic;
284      open my $fh, '<', "$current_dir/COPYRIGHT.txt" or die $!;
285      for my $line (<$fh>) {
286         $line =~ m/Automatically generated .* project-meta/i and $automatic++;
287         }
288      close $fh;
289
290      if (not $automatic) {
291         print "Warning: COPYRIGHT.txt already exists\n";
292         return;
293         }
294
295      print "Warning: update COPYRIGHT.txt\n";
296      }
297   
298   my $tt = Template->new(
299      INCLUDE_PATH   => '/usr/share/project-meta/template.d',
300      POST_CHOMP     => 1, # Remove space and carriage return after %]
301      );
302   my $msg_format = '';
303   $tt->process('COPYRIGHT.tt',
304      {
305         title       => $meta->{'project'}{'title'},
306         acronym     => $meta->{'project'}{'acronym'},
307         authorlist  => $meta->{'project'}{'authors'},
308         description => $meta->{'project'}{'short-description'},
309         licence     => $meta->{'public-dap'}{'data-licence'},
310         doi         => $meta->{'publication'}{'doi'},
311      }, \$msg_format) || die $tt->error;
312
313   open my $fh,  '>', "$current_dir/COPYRIGHT.txt" or die $!;
314   print $fh "$msg_format\n\n";
315   close $fh;
316   }
317
318################################################################
319
320sub cmd_list_licence {
321   opendir my $dh, '/usr/share/project-meta/licence.d/' or die $!;
322   for my $licence (readdir $dh) {
323      # Keep only file
324      next if not -f "/usr/share/project-meta/licence.d/$licence";
325     
326      # Keep only .txt file
327      next if not $licence =~ m/\.txt$/;
328
329      $licence =~ s/\.txt$//;
330      print "$licence\n";
331      }
332   closedir $dh;
333   }
334
335################################################################
336# documentation
337################################################################
338
339__END__
340
341=head1 NAME
342
343project-meta - opendata project metafile manager
344
345
346=head1 USAGE
347
348 project-meta help
349 project-meta version
350 project-meta check
351 project-meta make-link
352 project-meta make-zip
353 project-meta make-author
354 project-meta make-licence
355 project-meta make-copyright
356 project-meta make-licence
357 project-meta list-licence
358
359=head1 DESCRIPTION
360
361Project-Meta is a small tool to maintain a set of open data files.
362In order to help you in this task, C<project-meta> command has a set of action
363to generated and maintain many files in your dataset.
364
365Everything is declare in the metafile F<PROJECT-META.yml>.
366This YAML file must exist in your root projet folder.
367See L</METAFILE SPECIFICATION>.
368
369=head1 COMMANDS
370
371Some command are defined in the source code but are not documented here.
372Theses could be not well defined, not finished, not well tested...
373You can read the source code and use them at your own risk
374(like for all the Project-Meta code).
375
376=head2 check
377
378 project-meta check
379
380Check your F<PROJECT-META.yml> has the good key.
381If your metafile is not a valid YAML file,
382you can use C<yamllint> command to check just it's format.
383
384=head2 make-link
385
386 project-meta make-link
387
388Create UNIX soft links on the OpeNDAP folder to the real data.
389Files F<AUTHORS.txt>, F<LICENCE.txt> and F<COPYRIGHT.txt> are mandatory but could be generated (see below).
390The main keys use in the F<PROJECT-META.yml> are:
391
392=over
393
394=item * C<project/acronym>: the project short acronym, add to the OpeNDAP root folder
395
396=item * C<public-dap/dap-folder>: the OpeNDAP root folder
397
398=item * C<public-dap/data-set>: a list of files or folder to push
399
400=back
401
402=head2 make-zip
403
404 project-meta make-zip
405
406Create a ZIP archive with the open data set.
407Files F<AUTHORS.txt>, F<LICENCE.txt> and F<COPYRIGHT.txt> are mandatory but could be generated (see below).
408The main keys use in the F<PROJECT-META.yml> are:
409
410=over
411
412=item * C<project/acronym>: the project short acronym, use as root folder
413
414=item * C<public-dap/data-set>: a list of files or folder to push
415
416=back
417
418=head2 make-licence
419
420 project-meta make-licence
421
422Copy the licence file from the project-meta licence database at the current folder
423with the file name: F<LICENCE.txt>.
424
425The licence is defined in the F<PROJECT-META.yml> specification under the key C<public-dap/data-licence>.
426The list of possible licence is given with the command L</list-licence>.
427
428=head2 list-licence
429
430 project-meta list-licence
431
432Give the list of all the open data licence supported by the project-meta licence database.
433At this time the possible licence are:
434
435 license-ouverte-v2.0
436 open-database-license-v1.0
437
438Note that these licences are dedicated to open data.
439Please do not use open licence that have been written for code or documentation for data.
440
441
442=head1 METAFILE SPECIFICATION
443
444Each project must have an open data metafile which describe the project : C<PROJECT-META.yml>.
445The file is in YAML format because this is a human readable style of text file.
446
447You can find in the project-meta software a C<PROJECT-META.sample.yml> example.
448This one is actually the master reference specification!
449
450
451=head1 KNOWN BUGS
452
453 - not really check keys and tags before doing action!
454
455=head1 SEE ALSO
456
457yamllint
458
459
460=head1 AUTHOR
461
462Written by Gabriel Moreau, LEGI UMR5519, CNRS, Grenoble - France
463
464=head1 SPECIAL THANKS
465
466The list of people below did not directly contribute to project-meta's source code
467but provided me with some data, returned bugs
468or helped me in another task like having new ideas, specifications...
469Maybe I forgot your contribution in recent years,
470please forgive me in advance and send me an e-mail to correct this.
471
472Joel Sommeria, Julien Chauchat, Cyrille Bonamy, Antoine Mathieu.
473
474
475=head1 LICENSE AND COPYRIGHT
476
477Licence GNU GPL version 2 or later and Perl equivalent
478
479Copyright (C) 2017-2018 Gabriel Moreau <Gabriel.Moreau(A)univ-grenoble-alpes.fr>.
Note: See TracBrowser for help on using the repository browser.