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

Last change on this file since 215 was 214, checked in by g7moreau, 6 years ago
  • Change command make-link and remove-link to dap-publish and dap-unpublish
  • Property svn:executable set to *
File size: 17.0 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 libyaml-syck-perl libtemplate-perl libarchive-zip-perl
6# apt-get install yamllint libyaml-shell-perl # check YAML files
7
8use strict;
9use warnings;
10use version; our $VERSION = version->declare('0.0.10');
11
12use File::Copy qw(copy);   
13use YAML::Syck;
14use Getopt::Long();
15use Cwd();
16use Template;
17use Archive::Zip qw(:ERROR_CODES :CONSTANTS);
18
19
20my ($verbose);
21Getopt::Long::GetOptions(
22   'verbose' => \$verbose,
23   );
24
25
26my %CMD_DB = (
27   'help'                  => \&cmd_help,
28   'version'               => \&cmd_version,
29   'check'                 => \&cmd_check,
30   'dap-publish'           => \&cmd_dap_publish,
31   'dap-unpublish'         => \&cmd_dap_unpublish,
32   'make-zip'              => \&cmd_make_zip,
33   'make-allfiles'         => \&cmd_make_allfiles,
34   'make-file-author'      => \&cmd_make_file_author,
35   'make-file-copyright'   => \&cmd_make_file_copyright,
36   'make-file-license'     => \&cmd_make_file_license,
37   'list-license'          => \&cmd_list_license,
38   );
39
40################################################################
41# main program
42################################################################
43
44my $cmd = shift @ARGV || 'help';
45if (defined $CMD_DB{$cmd}) {
46   $CMD_DB{$cmd}->(@ARGV);
47   }
48else {
49   print {*STDERR} "project-meta: command $cmd not found\n\n";
50   $CMD_DB{'help'}->();
51   exit 1;
52   }
53
54exit;
55
56################################################################
57# subroutine
58################################################################
59
60sub print_ok {
61   my ($key, $test) = @_;
62   
63   printf "%-35s : %s\n", $key, $test ? 'yes' : 'no';
64   }
65
66################################################################
67
68sub addfolder2list {
69   my ($folderdb, $folder) = @_;
70   
71   return if $folder !~ m{/};
72   
73   $folder =~ s{/[^/]+$}{};
74
75   $folderdb->{$folder}++;
76   return addfolder2list($folderdb, $folder);
77   }
78
79################################################################
80# command
81################################################################
82
83sub cmd_help {
84   print <<'END';
85project-meta - opendata project metafile manager
86
87 project-meta help
88 project-meta version
89 project-meta check
90 project-meta dap-publish
91 project-meta dap-unpublish
92 project-meta make-zip
93 project-meta make-allfiles
94 project-meta list-license
95 project-meta make-file-license
96 project-meta make-file-author
97 project-meta make-file-copyright
98END
99   }
100
101################################################################
102
103sub cmd_version {
104   print "$VERSION\n";
105   }
106
107################################################################
108
109sub cmd_check {
110   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
111
112   my $acronym     = $meta->{'project'}{'acronym'};
113   my $current_dir = Cwd::getcwd();
114   my $dap_folder  = $meta->{'public-dap'}{'dap-folder'};
115
116   print_ok 'project/acronym',                  $acronym =~ m{\d\d\w[\w\d_/]+};
117   print_ok 'public-dap/dap-folder',            $dap_folder ne '' and $dap_folder =~ m{^/};
118   print_ok 'dap-folder not match current_dir', $dap_folder !~ m{$current_dir};
119
120   #print YAML::Syck::Dump($meta);
121   }
122
123################################################################
124
125sub cmd_dap_publish {
126   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
127   my $current_dir = Cwd::getcwd();
128   my $acronym     = $meta->{'project'}{'acronym'};
129   my $dap_folder  = $meta->{'public-dap'}{'dap-folder'};
130   my $data_set    = $meta->{'public-dap'}{'data-set'};
131
132   push @{$data_set}, 'AUTHORS.txt', 'COPYRIGHT.txt', 'LICENSE.txt';
133   {
134      # Remove doublon
135      my %seen = ();
136      @{$data_set} = grep { ! $seen{$_}++ } @{$data_set};
137      }
138
139   # Create a list of the folder
140   my %folders;
141   for my $dataset (@{$data_set}) {
142      addfolder2list(\%folders, $dataset);
143      }
144
145   print "chmod o+rX,o-w '$current_dir'\n";
146   print "mkdir -p '$dap_folder/$acronym'\n" if not -d "$dap_folder/$acronym";
147   for my $folder (sort keys %folders) {
148      print "chmod o+rX,o-w '$current_dir/$folder'\n";
149      print "mkdir '$dap_folder/$acronym/$folder'\n" if -d "$current_dir/$folder";
150      }
151
152   for my $dataset (@{$data_set}) {
153      if ($dataset =~ m{/}) {
154         # sub-folder case
155         my $folder = $dataset =~ s{/[^/]+$}{}r;
156         print "chmod -R o+rX,o-w '$current_dir/$dataset'\n";
157         print "ln --symbolic --target-directory '$dap_folder/$acronym/$folder/' '$current_dir/$dataset'\n";
158         }
159      else {
160         # Root case
161         print "ln --symbolic --target-directory '$dap_folder/$acronym/' '$current_dir/$dataset'\n";
162         }
163
164      }
165   print "chmod -R o+rX,o-w '$dap_folder/$acronym/'\n";
166   }
167
168################################################################
169
170sub cmd_dap_unpublish {
171   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
172   my $current_dir = Cwd::getcwd();
173   my $acronym     = $meta->{'project'}{'acronym'};
174   my $dap_folder  = $meta->{'public-dap'}{'dap-folder'};
175
176   die "Error: DAP folder match current folder" if $dap_folder =~ m{$current_dir} or $current_dir =~ m{$dap_folder};
177
178   print "find '$dap_folder/$acronym/' -type l -o -type d -exec ls -l {} \+\n";
179   print "find '$dap_folder/$acronym/' -type l -delete\n";
180   print "find '$dap_folder/$acronym/' -type d -delete\n";
181   }
182
183################################################################
184
185sub cmd_make_zip {
186   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
187   my $current_dir = Cwd::getcwd();
188   my $data_set    = $meta->{'public-dap'}{'data-set'};
189   my $acronym     = $meta->{'project'}{'acronym'};
190
191   push @{$data_set}, 'AUTHORS.txt', 'COPYRIGHT.txt', 'LICENSE.txt';
192   {
193      # Remove doublon
194      my %seen = ();
195      @{$data_set} = grep { ! $seen{$_}++ } @{$data_set};
196      }
197
198   # Create a Zip file
199   my $zip = Archive::Zip->new();
200
201   for my $dataset (@{$data_set}) {
202      if (-d $dataset) {
203         # Folder case
204         $zip->addTree($dataset, "$acronym/$dataset");
205         }
206      elsif (-f $dataset) {
207         # File case
208         $zip->addFile($dataset, "$acronym/$dataset");
209         }
210      else {
211         # Strange case
212         print "Error: entry $dataset doesn't exists\n";
213         }
214      }
215
216   my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime time;
217   $year += 1900;
218   $mon++;
219   my $date = sprintf '%04i%02i%02i-%02i%02i', $year, $mon, $mday, $hour, $min;
220
221   # Save the Zip file
222   unless ($zip->writeToFileNamed("$current_dir/$acronym--$date.zip") == AZ_OK) {
223      die 'Error: zip write error';
224      }
225   }
226
227################################################################
228
229sub cmd_make_allfiles {
230   cmd_make_file_author();
231   cmd_make_file_license();
232   cmd_make_file_copyright();
233   }
234
235################################################################
236
237sub cmd_make_file_author {
238   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
239
240   my $current_dir = Cwd::getcwd();
241
242   my $acronym    = $meta->{'project'}{'acronym'};
243   my $authors_list = $meta->{'project'}{'authors'};
244
245   if (-f "$current_dir/AUTHORS.txt") {
246      # Test for manual or automatically generated file
247      # Automatically generated file by project-meta
248      my $automatic;
249      open my $fh, '<', "$current_dir/AUTHORS.txt" or die $!;
250      for my $line (<$fh>) {
251         $line =~ m/Automatically generated .* project-meta/i and $automatic++;
252         }
253      close $fh;
254
255      if (not $automatic) {
256         print "Warning: AUTHORS.txt already exists\n";
257         return;
258         }
259
260      print "Warning: update AUTHORS.txt\n";
261      }
262
263   my $tt = Template->new(INCLUDE_PATH => '/usr/share/project-meta/template.d');
264   my $msg_format = '';
265   $tt->process('AUTHORS.tt',
266      {
267         acronym    => $acronym,
268         authorlist => $authors_list,
269      }, \$msg_format) || die $tt->error;
270
271   open my $fh,  '>', "$current_dir/AUTHORS.txt" or die $!;
272   print $fh "$msg_format\n\n";
273   close $fh;
274   }
275
276################################################################
277
278sub cmd_make_file_license {
279   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
280
281   my $current_dir = Cwd::getcwd();
282
283   if (-f "$current_dir/LICENSE.txt") {
284      print "Warning: LICENSE.txt already exists\n";
285      return;
286      }
287
288   my $license = $meta->{'public-dap'}{'data-license'};
289
290   if (not -f "/usr/share/project-meta/license.d/$license.txt") {
291      print "Error: license $license doesn't exists in project-meta database\n";
292      exit 1;
293      }
294
295   copy("/usr/share/project-meta/license.d/$license.txt", "$current_dir/LICENSE.txt")
296      or die "Error: license copy failed - $!";
297
298   print "Info: LICENSE.txt file create\n";
299   return;
300   }
301
302################################################################
303
304sub cmd_make_file_copyright {
305   my $meta = YAML::Syck::LoadFile("PROJECT-META.yml");
306
307   my $current_dir = Cwd::getcwd();
308
309   if (-f "$current_dir/COPYRIGHT.txt") {
310      # Test for manual or automatically generated file
311      # Automatically generated file by project-meta
312      my $automatic;
313      open my $fh, '<', "$current_dir/COPYRIGHT.txt" or die $!;
314      for my $line (<$fh>) {
315         $line =~ m/Automatically generated .* project-meta/i and $automatic++;
316         }
317      close $fh;
318
319      if (not $automatic) {
320         print "Warning: COPYRIGHT.txt already exists\n";
321         return;
322         }
323
324      print "Warning: update COPYRIGHT.txt\n";
325      }
326   
327   my $tt = Template->new(
328      INCLUDE_PATH   => '/usr/share/project-meta/template.d',
329      POST_CHOMP     => 1, # Remove space and carriage return after %]
330      );
331   my $msg_format = '';
332   $tt->process('COPYRIGHT.tt',
333      {
334         title       => $meta->{'project'}{'title'},
335         acronym     => $meta->{'project'}{'acronym'},
336         authorlist  => $meta->{'project'}{'authors'},
337         description => $meta->{'project'}{'short-description'},
338         license     => $meta->{'public-dap'}{'data-license'},
339         doi         => $meta->{'publication'}{'doi'},
340      }, \$msg_format) || die $tt->error;
341
342   open my $fh,  '>', "$current_dir/COPYRIGHT.txt" or die $!;
343   print $fh "$msg_format\n\n";
344   close $fh;
345   }
346
347################################################################
348
349sub cmd_list_license {
350   opendir my $dh, '/usr/share/project-meta/license.d/' or die $!;
351   for my $license (readdir $dh) {
352      # Keep only file
353      next if not -f "/usr/share/project-meta/license.d/$license";
354     
355      # Keep only .txt file
356      next if not $license =~ m/\.txt$/;
357
358      $license =~ s/\.txt$//;
359      print "$license\n";
360      }
361   closedir $dh;
362   }
363
364################################################################
365# documentation
366################################################################
367
368__END__
369
370=head1 NAME
371
372project-meta - opendata project metafile manager
373
374
375=head1 USAGE
376
377 project-meta help
378 project-meta version
379 project-meta check
380 project-meta dap-publish
381 project-meta dap-unpublish
382 project-meta make-zip
383 project-meta list-license
384 project-meta make-file-license
385 project-meta make-file-author
386 project-meta make-file-copyright
387
388
389=head1 DESCRIPTION
390
391Project-Meta is a small tool to maintain a set of open data files.
392In order to help you in this task, C<project-meta> command has a set of action
393to generated and maintain many files in your dataset.
394
395Everything is declare in the metafile F<PROJECT-META.yml>.
396This YAML file must exist in your root projet folder.
397See L</METAFILE SPECIFICATION>.
398
399
400=head1 COMMANDS
401
402Some command are defined in the source code but are not documented here.
403Theses could be not well defined, not finished, not well tested...
404You can read the source code and use them at your own risk
405(like for all the Project-Meta code).
406
407=head2 check
408
409 project-meta check
410
411Check your F<PROJECT-META.yml> has the good key.
412If your metafile is not a valid YAML file,
413you can use C<yamllint> or C<ysh> commands to check just it's format.
414
415=head2 dap-publish
416
417 project-meta dap-publish
418
419Publish data on an OpeNDAP server.
420Because data can be very large,
421This command just create UNIX soft links on the OpeNDAP folder to the real data.
422There is no copy.
423Files F<AUTHORS.txt>, F<LICENSE.txt> and F<COPYRIGHT.txt> are mandatory but could be generated (see below).
424The main keys use in the F<PROJECT-META.yml> are:
425
426=over
427
428=item * C<project/acronym>: the project short acronym, add to the OpeNDAP root folder
429
430=item * C<public-dap/dap-folder>: the OpeNDAP root folder
431
432=item * C<public-dap/data-set>: a list of files or folder to push
433
434=back
435
436Because this command could be dangerous, it does nothing!
437It print on terminal shell command to be done.
438You have to verify ouput before eval it.
439
440 project-meta dap-publish
441 project-meta dap-publish | bash
442
443=head2 dap-unpublish
444
445 project-meta dap-unpublish
446
447Unpublish data from the OpeNDAP server.
448In practice, it remove links in OpeNDAP folder for that projet.
449Because command C<rm> is always dangerous,
450we use here the command C<find> limited to folder and link.
451
452Please verify the returned values before excuted it with the C<-delete> option.
453
454=head2 make-zip
455
456 project-meta make-zip
457
458Create a ZIP archive with the open data set.
459Files F<AUTHORS.txt>, F<LICENSE.txt> and F<COPYRIGHT.txt> are mandatory but could be generated (see below).
460The main keys use in the F<PROJECT-META.yml> are:
461
462=over
463
464=item * C<project/acronym>: the project short acronym, use as root folder
465
466=item * C<public-dap/data-set>: a list of files or folder to push
467
468=back
469
470=head2 make-allfiles
471
472 project-meta make-allfiles
473
474Generate or update all files: F<AUTHORS.txt>, F<COPYRIGHT.txt> and F<LICENSE.txt>.
475This command is just a shortcut for L</make-file-author>, L</make-file-copyright> and L</make-file-license>.
476
477
478=head2 list-license
479
480 project-meta list-license
481
482Give the list of all the open data licenses supported by the project-meta license database.
483At this time the possible licenses are:
484
485=over
486
487=item * L<creative-common-zero-v1.0|https://creativecommons.org/publicdomain/zero/1.0/legalcode.txt>
488        (like public domain)
489
490=item * L<creative-common-attribution-v4.0|https://creativecommons.org/licenses/by/4.0/legalcode.txt>
491        (copyleft - allow users to freely share and adapt while maintaining this same freedom for others)
492
493=item * L<licence-ouverte-v2.0|https://www.etalab.gouv.fr/wp-content/uploads/2017/04/ETALAB-Licence-Ouverte-v2.0.pdf>
494        (copyleft - opendata french goverment)
495
496=item * L<open-database-license-v1.0|https://opendatacommons.org/files/2018/02/odbl-10.txt>
497        (copyleft - allow users to freely share, modify, and use the database while maintaining this same freedom for others)
498
499=back
500
501Note that these licenses are dedicated to open data.
502Please do not use an open license that would have been thought for source code or documentation and not for open data.
503
504=head2 make-file-license
505
506 project-meta make-file-license
507
508Copy the license file from the project-meta license database at the current folder
509with the file name: F<LICENSE.txt>.
510
511The license is defined in the F<PROJECT-META.yml> specification under the key C<public-dap/data-license>.
512The list of possible license is given with the command L</list-license>.
513
514=head2 make-file-author
515
516 project-meta make-file-author
517
518Create or update the F<AUTHORS.txt> file at the current folder.
519Authors data are extracted from the C<PROJECT-META.yml> file.
520
521=head2 make-file-copyright
522
523 project-meta make-file-copyright
524
525Create or update the F<COPYRIGHT.txt> file at the current folder.
526Authors, license and copyright data are extracted from the C<PROJECT-META.yml> file.
527
528
529=head1 METAFILE SPECIFICATION
530
531Each project must have an open data metafile which describe the project : C<PROJECT-META.yml>.
532The file is in YAML format because this is a human readable style of text file.
533
534You can find in the project-meta software a
535L<PROJECT-META.sample.yml|http://servforge.legi.grenoble-inp.fr/pub/soft-trokata/project-meta/PROJECT-META.sample.yml> example.
536This one is actually the master reference specification!
537
538
539=head1 KNOWN BUGS
540
541 - not really check keys and tags before doing action!
542
543
544=head1 SEE ALSO
545
546yamllint(1), ysh(1), YAML, Archive::Zip
547
548In Debian GNU/Linux distribution, packages for C<yamllint> and C<ysh> are:
549
550=over
551
552=item * C<yamllint> - Linter for YAML files (Python)
553
554=item * C<libyaml-shell-perl> - YAML test shell (Perl)
555
556=back
557
558
559Own project ressources:
560
561=over
562
563=item * L<Web site|http://servforge.legi.grenoble-inp.fr/projects/soft-trokata/wiki/SoftWare/ProjectMeta>
564
565=item * L<Online Manual|http://servforge.legi.grenoble-inp.fr/pub/soft-trokata/project-meta/project-meta.html>
566
567=item * L<SVN repository|http://servforge.legi.grenoble-inp.fr/svn/soft-trokata/trunk/project-meta>
568
569=back
570
571
572=head1 AUTHOR
573
574Written by Gabriel Moreau, LEGI UMR5519, CNRS, Grenoble - France
575
576
577=head1 SPECIAL THANKS
578
579The list of people below did not directly contribute to project-meta's source code
580but provided me with some data, returned bugs
581or helped me in another task like having new ideas, specifications...
582Maybe I forgot your contribution in recent years,
583please forgive me in advance and send me an e-mail to correct this.
584
585Joel Sommeria, Julien Chauchat, Cyrille Bonamy, Antoine Mathieu.
586
587
588=head1 LICENSE AND COPYRIGHT
589
590License GNU GPL version 2 or later and Perl equivalent
591
592Copyright (C) 2017-2018 Gabriel Moreau <Gabriel.Moreau(A)univ-grenoble-alpes.fr>.
Note: See TracBrowser for help on using the repository browser.