App-Cme-1.026/0000755000175000017500000000000013216003010011370 5ustar domidomiApp-Cme-1.026/MANIFEST0000644000175000017500000000127013216003010012521 0ustar domidomi# This file was automatically generated by Dist::Zilla::Plugin::Manifest v6.010. Build.PL CONTRIBUTING.md Changes LICENSE MANIFEST MANIFEST.SKIP META.json META.yml README.install.pod README.pod bin/cme build-from-git.md contrib/bash_completion.cme lib/App/Cme.pm lib/App/Cme/Command/check.pm lib/App/Cme/Command/dump.pm lib/App/Cme/Command/edit.pm lib/App/Cme/Command/fix.pm lib/App/Cme/Command/fusefs.pm lib/App/Cme/Command/gen_class_pod.pm lib/App/Cme/Command/list.pm lib/App/Cme/Command/migrate.pm lib/App/Cme/Command/modify.pm lib/App/Cme/Command/run.pm lib/App/Cme/Command/search.pm lib/App/Cme/Command/shell.pm lib/App/Cme/Command/update.pm lib/App/Cme/Common.pm t/cme-command.t weaver.ini App-Cme-1.026/README.install.pod0000644000175000017500000000154113216003010014477 0ustar domidomi=head1 Installation =head2 Debian or Ubuntu L is provided as Debian package. Just type sudo apt-get install cme Or use your favorite installer =head2 Fedora or RedHat L is now packaged by RedHat. Type: sudo yum install perl-App-Cme =head2 Mac OSX L is provided as ppm package by L: =over =item * Install L =item * Update your $PATH variable to run ActiveState's perl =item * Run ppm to install L =back =head2 Windows You can also install L from L. See the instructions for Mac OSX for details. =head2 Other systems For other systems, you should install L from CPAN: cpanp install App::Cme App-Cme-1.026/LICENSE0000644000175000017500000006012513216003010012401 0ustar domidomiThis software is Copyright (c) 2017 by Dominique Dumont. This is free software, licensed under: The GNU Lesser General Public License, Version 2.1, February 1999 The GNU Lesser General Public License (LGPL) Version 2.1, February 1999 (The master copy of this license lives on the GNU website.) Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS App-Cme-1.026/t/0000755000175000017500000000000013216003010011633 5ustar domidomiApp-Cme-1.026/t/cme-command.t0000644000175000017500000002152213216003010014202 0ustar domidomi# -*- cperl -*- use strict; use warnings; use utf8; use 5.10.1; use open ':std', ':encoding(utf8)'; use Encode; use Path::Tiny; use Probe::Perl; use Test::Command 0.08; use Test::More; use Test::File::Contents; use App::Cmd::Tester; use App::Cme ; if ( $^O !~ /linux|bsd|solaris|sunos/ ) { plan skip_all => "Test with system() in build systems don't work well on this OS ($^O)"; } my $arg = shift || ''; my ( $log, $show ) = (0) x 2; my $trace = $arg =~ /t/ ? 1 : 0; Config::Model::Exception::Any->Trace(1) if $arg =~ /e/; ## testing exit status # pseudo root where config files are written by config-model my $wr_root = path('wr_root'); # cleanup before tests $wr_root -> remove_tree; my $test1 = 'popcon1'; my $wr_dir = $wr_root->child($test1); my $conf_dir = $wr_dir->child('/etc'); $conf_dir->mkpath; my $conf_file = $conf_dir->child("popularity-contest.conf"); # created with -backup option my $backup_file = $conf_dir->child("popularity-contest.conf.old"); my $perl_path = Probe::Perl->find_perl_interpreter(); my $perl_cmd = $perl_path . ' -Ilib ' . join( ' ', map { "-I$_" } Probe::Perl->perl_inc() ); # debian continuous integ tests run tests out of source. Must use system cme my $cme_cmd = -e 'bin/cme' ? "$perl_cmd bin/cme" : 'cme' ; note("cme is invoked with: '$cme_cmd'" ); subtest "list command" => sub { my @test_cmd = qw/list/; my $result = test_app( 'App::Cme' => \@test_cmd ); say "-- stdout --\n", $result->stdout,"-----" if $trace; is($result->error, undef, 'threw no exceptions'); }; subtest "modification without config file" => sub { my $test_cmd = [ qw/modify popcon/, '-root-dir' => $wr_dir->stringify, "PARTICIPATE=yes" ]; my $oops = test_app( 'App::Cme' => $test_cmd ); is ($oops->exit_code, 2, 'error detected' ); like($oops->error, qr/cannot find configuration file/, 'missing config file detected' ); }; # put popcon data in place my @orig = ; $conf_file->spew_utf8(@orig); subtest "minimal modification" => sub { # test minimal modif (re-order) my @test_cmd = (qw/modify popcon -save -backup -canonical -root-dir/, $wr_dir->stringify); my $ok = test_app( 'App::Cme' => \@test_cmd ); is ($ok->exit_code, 0, 'all went well' ) or diag("Failed command cme @test_cmd"); is($ok->error, undef, 'threw no exceptions'); say $ok->stdout; file_contents_like $conf_file->stringify, qr/cme/, "updated header"; # with perl 5.14 5.16, IO::Handle writes an extra \n with print. my $re = $^V lt 5.18.1 ? qr/yes"\n+MY/ : qr/yes"\nMY/; file_contents_like $conf_file->stringify, $re, "reordered file"; file_contents_unlike $conf_file->stringify, qr/removed/, "double comment is removed"; # check backup ok($backup_file->is_file, "backup file was created"); file_contents_like $backup_file->stringify, qr/should be removed/, "backup file contains original comment"; }; subtest "modification with wrong parameter" => sub { my @test_cmd = (qw/modify popcon -root-dir/, $wr_dir->stringify, qq/PARITICIPATE=yes/); my $oops = test_app( 'App::Cme' => \@test_cmd ); isnt ($oops->exit_code, 0, 'error detected' ); like($oops->error.'' , qr/object/, 'check unknown element' ); isnt( $oops->exit_code, 0, 'wrong parameter detected' ); }; subtest "modification with good parameter" => sub { # use -save to force a file save to update file header my @test_cmd = (qw/modify popcon -save -root-dir/, $wr_dir->stringify, qq/PARTICIPATE=yes/); my $ok = test_app( 'App::Cme' => \@test_cmd ); is( $ok->exit_code, 0, 'all went well' ) or diag("Failed command @test_cmd"); file_contents_like $conf_file->stringify, qr/cme/, "updated header"; file_contents_unlike $conf_file->stringify, qr/removed`/, "double comment is removed"; }; subtest "search" => sub { my @test_cmd = (qw/search popcon -root-dir/, $wr_dir->stringify, qw/-search y -narrow value/); my $search = test_app( 'App::Cme' => \@test_cmd ); is( $search->error, undef, 'threw no exceptions'); is( $search->exit_code, 0, 'search went well' ) or diag("Failed command @test_cmd"); like( $search->stdout, qr/PARTICIPATE/, "got PARTICIPATE" ); like( $search->stdout, qr/USEHTTP/, "got USEHTTP" ); }; subtest "modification with utf8 parameter" => sub { my $utf8_name = "héhôßœ"; my @test_cmd = ((qw/modify popcon -root-dir/, $wr_dir->stringify), encode('UTF-8',qq/MY_HOSTID="$utf8_name"/) ); my $ok = test_app( 'App::Cme' => \@test_cmd ); is( $ok->error, undef, 'threw no exceptions'); is( $ok->exit_code, 0, 'all went well' ) or diag("Failed command @test_cmd"); file_contents_like $conf_file->stringify, qr/$utf8_name/, "updated MY_HOSTID with weird utf8 hostname" ,{ encoding => 'UTF-8' }; }; my @script_tests = ( { label => "modification with a script and args", script => [ "app: popcon", 'load ! MY_HOSTID=\$name$name'], args => [qw!--arg name=foobar!], test => qr/"\$namefoobar"/ }, { label => "modification with a script and a default value", script => [ "app: popcon", "default: name foobar", 'load ! MY_HOSTID=\$name$name'], test => qr/"\$namefoobar"/ }, { label => "modification with a script and a var that uses a default value", script => [ "app: popcon", "default: defname foobar", 'var: $var{name} = $args{defname}', 'load ! MY_HOSTID=\$name$name' ], test => qr/"\$namefoobar"/ }, { label => "modification with a script and var section", script => [ "app: popcon", 'var: $var{name}="foobar2"','load ! MY_HOSTID=\$name$name'], test => qr/"\$namefoobar2"/ }, { label => "modification with a script and var section which uses args", script => [ "app: popcon", 'var: $var{name}=$args{fooname}."bar2"','load ! MY_HOSTID=\$name$name'], args => [qw/--arg fooname=foo/], test => qr/"\$namefoobar2"/ }, { label => "modification with a Perl script run by cme run with args", script => [ "use Config::Model qw(cme);", 'my ($opt,$val,$name) = @ARGV;', 'cme(application => "popcon", root_dir => $val)->modify("! MY_HOSTID=\$name$name");' ], args => ['foobar3'], test => qr/"\$namefoobar3"/ }, ); # test cme run real script with arguments foreach my $test ( @script_tests) { subtest $test->{label} => sub { my $script = $wr_dir->child('my-script.cme'); $script->spew_utf8( map { "$_\n"} @{$test->{script}}); my $cmd = [ run => $script->stringify, '-root-dir' => $wr_dir->stringify, @{$test->{args} // []} ]; note("cme command: cme @$cmd"); my $ok = test_app('App::Cme' => $cmd); is( $ok->error, undef, 'threw no exceptions'); is( $ok->exit_code, 0, "all went well" ) or diag("Failed command: @$cmd"); file_contents_like $conf_file->stringify, $test->{test}, "updated MY_HOSTID with script" ,{ encoding => 'UTF-8' }; }; } # test failure case for run script my @bad_script_tests = ( { label => "modification with a Perl script run by cme run with missing arg", script => [ "app: popcon", 'load ! MY_HOSTID=\$name$name'], args => [], error_regexp => qr/use option '-arg name=xxx'/ }, { label => "modification with a Perl script run by cme run with 2 missing args", script => [ "app: popcon", 'load ! MY_HOSTID=$name1$name2'], args => [], error_regexp => qr/use option '-arg name1=xxx -arg name2=xxx'/ }, { label => "modification with a Perl script run by cme run with missing args in var line", script => [ "app: popcon", 'var: $var{name} = $args{name1}.$args{name2}', 'load: ! MY_HOSTID=$name'], args => [], error_regexp => qr/use option '-arg name1=xxx -arg name2=xxx'/ }, ); foreach my $test ( @bad_script_tests) { subtest $test->{label} => sub { my $script = $wr_dir->child('my-script.cme'); $script->spew_utf8( map { "$_\n"} @{$test->{script}}); my $cmd = [ run => $script, '-root-dir' => $wr_dir->stringify, @{$test->{args}} ]; note("cme command: @$cmd"); my $oops = test_app('App::Cme' => $cmd); isnt( $oops->exit_code, 0, 'wrong command detected' ); my $re = $test->{error_regexp}; like( $oops->error.'', $re , "check error message of cme command"); }; } done_testing; __END__ # Config file for Debian's popularity-contest package. # # To change this file, use: # dpkg-reconfigure popularity-contest ## should be removed MY_HOSTID="aaaaaaaaaaaaaaaaaaaa" # we participate PARTICIPATE="yes" USEHTTP="yes" # always http DAY="6" App-Cme-1.026/weaver.ini0000644000175000017500000000022513216003010013361 0ustar domidomi[@Default] [-Transformer] transformer = List [Support] perldoc = 0 bugs = metadata websites = search,anno,ratings,kwalitee,testers,testmatrix,deps App-Cme-1.026/bin/0000755000175000017500000000000013216003010012140 5ustar domidomiApp-Cme-1.026/bin/cme0000755000175000017500000002550513216003010012641 0ustar domidomi#!/usr/bin/env perl # # This file is part of App-Cme # # This software is Copyright (c) 2017 by Dominique Dumont. # # This is free software, licensed under: # # The GNU Lesser General Public License, Version 2.1, February 1999 # # PODNAME: cme # ABSTRACT: Check or edit configuration data with Config::Model # see perlunicook use utf8; # so literals and identifiers can be in UTF-8 use v5.12; # or later to get "unicode_strings" feature use strict; # quote strings, declare variables use warnings; # on by default use warnings qw(FATAL utf8); # fatalize encoding glitches use open qw(:std :utf8); # undeclared streams in UTF-8 use charnames qw(:full :short); # unneeded in v5.16 use App::Cme; App::Cme->run; __END__ =pod =encoding UTF-8 =head1 NAME cme - Check or edit configuration data with Config::Model =head1 VERSION version 1.026 =head1 SYNOPSIS # general synopsis cme [ global_options ] command application [ options ] [ file ] [ modification_instructions ] # edit dpkg config with GUI (and Config::Model::Dpkg) cme edit dpkg # read data from arbitrary file (for model read from alternate file) cme check dpkg-copyright -file path/to/file # edit /etc/sshd_config (with Config::Model::OpenSsh) sudo cme edit sshd # edit ~/.ssh/config (with Config::Model::OpenSsh) cme edit ssh # just check the validity of a file. Both commands are equivalent cme check multistrap file.conf cme check multistrap -file file.conf # check dpkg files, update deprecated parameters and save cme migrate dpkg # like migrate, but also apply all suggested fixes cme fix dpkg # modify configuration with command line cme modify dpkg source 'format="(3.0) quilt"' # likewise with an application that accepts file override cme modify dpkg-copyright 'Comment="Modified with cme"' # edit a file (file name specification is mandatory here) cme edit multistrap my.conf # map conf data to a fuse file system cme fusefs multistrap my.conf -d fuse_dir # likewise for dpkg data cme fusefs dpkg -d fuse_dir # list all available applications (depends on your installation) cme list =head1 DESCRIPTION Depending on the command described below, C program will use Config::Model configuration descriptions to check or modify or fix configuration files. The 3rd parameter specify the application you want to work on. Most of the time, the relevant configuration file(s) will be found by cme. This is the most simple case. For instance: sudo cme check popcon Some application like C have no constraint on the configuration file name and will require you to specify your configuration file name: cme check multistrap raspbian.conf or cme check multistrap -file raspbian.conf =head1 Configuration file specification The configuration of an application can take different forms. Either several files (like debian packages), a single file with a predefined file (popcon), or a single file with an arbitrary file name (multistrap). When needed the configuration file name is specified as the 3rd command argument, i.e. C. This applies if the application requires a configuration file name (like multistrap), or if the application allows configuration file override. When the overridden file is C<-> (a single dash), the configuration is read from STDIN. The resulting file may be written on STDOUT. I.e. cat debian/confrol | cme migrate dpkg-control -save -file - will output a fixed control file on STDOUT. =head1 Main commands This section describes the main commands shipped with cme. Other Config::Model extensions can bring more command. Run C to get the list of available commands on your system. =head2 list Show a list all applications where a model is available. This list depends on installed Config::Model modules. =head2 edit Edit a configuration. By default, a Tk GUI will be opened If L is installed. See L. =head2 shell Edit the configuration with a shell like interface. See L. =head2 check Checks the content of the configuration file of an application. See L. =head2 migrate Update deprecated parameters (old value are saved to new parameters) and save the new configuration. See L. =head2 fix Migrate data and fix warnings. See L. =head2 modify Modify a configuration file with the values passed on the command line. See L. =head2 update Update the content of the configuration file from external data. Currently, only dpkg-copyright model support update sub command. See L. =head2 search Search configuration data for a specific string. See L. =head2 fusefs Map the configuration file content to a FUSE virtual file system on a directory specified with option C<-fuse-dir>. Modifications done in the fuse file system are saved to the configuration file when the C is run. =head1 Global options The following options are available for all commands: =over =item -create Perform the operation even if the configuration file is missing. This may be used to create a minimal configuration file. This option is disabled by default as a missing configuration file often indicates an error during the installation of the application. =item -file For model that support it, specify an alternate file to read and write the configuration. Use "C<->" to read from STDIN. You can use this option for model that require the target file to be specified (e.g. multitrap model), but file can in this case be also specified with the 4th command argument. E.g. these 2 commands habe the same effect: cme check multistrap foo.conf cme check multistrap -file foo.conf =item -force-load Load file even if error are found in data. Bad data are discarded =item -canonical Write config data back using model order. By default, write items back using the order found in the configuration file. This feature is experimental and not supported by all backends. =item -backup Create a backup of configuration files before saving. By default, C will be appended to the backup file. I.e. C will be backed up as C. You can specify an alternate suffix. For instance C<-backup dpkg-old>. =item -save Force a save even if no change was done. Useful to reformat the configuration file. =item -strict When set, cme will exit 1 if warnings are found during check (of left after fix) =item -verbose Set to C<1>, C<2>, C<3> or C, C, C to get more verbose output. These valiues can be abbreviated. Example: cme check ssh -v 1 cme check ssh -v i cme check ssh -v info =back =head1 Advanced options =over =item -model-dir Specify an alternate directory to find model files. Mostly useful for tests. =item -root-dir Specify a pseudo root directory to read and write the configuration files. (Actual default directory and file names depends on the model (See C<-model> option). For instance, if you specify C<~/mytest>, the C files will be written in C<~/mytest/etc/ssh/> directory. =item -stack-trace Provides a full stack trace when exiting on error. =item -try-app-as-model When set, try to load a model using directly the application name specified as 3rd parameter on the command line. Experimental. =back =head1 Embedding cme You can use cme from another program by using C<-ui simple> option. This way you will be able to send command on the standard input of C and get the results from the standard output. =head1 Logging All Config::Model logging is now based on L. Logging can be configured in the following files: =over =item * ~/.log4config-model =item * /etc/log4config-model.conf =back A sample of a C<.log4config-model> is provided in contrib directory in C distribution of on L Without these files, the following Log4perl config is used: log4perl.rootLogger=WARN, Screen log4perl.logger.Model.Legacy = INFO, SimpleScreen log4perl.additivity.Model.Legacy = 0 log4perl.appender.Screen = Log::Log4perl::Appender::Screen log4perl.appender.Screen.stderr = 0 log4perl.appender.Screen.layout = Log::Log4perl::Layout::PatternLayout log4perl.appender.Screen.layout.ConversionPattern = %M %m (line %L)%n log4perl.appender.SimpleScreen = Log::Log4perl::Appender::Screen log4perl.appender.SimpleScreen.stderr = 0 log4perl.appender.SimpleScreen.layout = Log::Log4perl::Layout::PatternLayout log4perl.appender.SimpleScreen.layout.ConversionPattern = %p: %m%n log4perl.oneMessagePerAppender = 1 Log4perl uses the following categories: =over =item Anything =item Anything::Change Trace change notification through configuration tree and instance. =item Backend =item Backend::Debian::Dpkg =item Backend::Debian::Dpkg::Control =item Backend::Debian::Dpkg::Copyright =item Backend::Fstab =item Backend::IniFile =item Backend::PlainFile =item Backend::ShellVar =item Backend::Yaml =item FuseUI =item Instance =item Loader =item Model::Searcher =item Tree::Element::CheckList =item Tree::Element::Id =item Tree::Element::Id::Hash =item Tree::Element::Id::List =item Tree::Element::Value =item Tree::Element::Value::Dependency =item Tree::Node =item Tree::Node::Warped =item ValueComputer =item Warper =item Iterator =item Model =back More categories will come. =head1 EXIT CODE cme exits 0 when no errors are found. Exit 1 otherwise. =head1 BUGS =head2 Configuration models can lag behind the target application If a configuration model is not up-to-date, you will get errors complaining about unknown parameters. In such a case, please file a bug on L or fix the model and send a pull request. You can see this L to learn how to fix a model. =head1 SUPPORT For support, please check the following resources: =over =item * The config-model users mailing list: config-model-users at lists.sourceforge.net =item * The config-model wiki: L =back =head1 FEEDBACKS Feedback from users are highly desired. If you find this module useful, please share your use cases, success stories with the author or with the config-model- users mailing list. =head1 SEE ALSO L, L, L, L, L, L, L =head1 AUTHOR Dominique Dumont =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2017 by Dominique Dumont. This is free software, licensed under: The GNU Lesser General Public License, Version 2.1, February 1999 =cut App-Cme-1.026/Changes0000644000175000017500000001657013216003010012674 0ustar domidomi1.026 2017-12-18 New experimental feature: * By default, Shellvar backend from Config::Model 2.116 keeps parameter order when writing back configuration data. This broke cme test that check that data is written with canonical order * add -canonical option to let data be written back according to model order (i.e. the previous behavior of shellvar backend) * Fix test broken by new ShellVar behavior (using -canonical option) * Depends on Config::Model 2.116 (for -canonical option) On-going deprecation of obscure multi backend feature * cme: remove obsolete -backend option (since only one backend can now be available, there's no need to an option to choose an alternative backend) 1.025 2017-12-14 Some new features for the rather new 'run' subcommand: * add -cat option option to show the script * -doc option also shows commit message New feature for the older 'dump' subcommand * -format accepts yaml or yml * -format value is now case insensitive * accept -format cds (alias to cml) Misc: * improve doc of modify command 1.024 2017-10-22 Some new features for the rather new 'run' subcommand: * add --no-commit option (Debian #877986) * add --commit option * can now specify default value for script args Other bug fixes: * run: remove warning about Log4Perl initialisation * run: fix option description * cme: update Log4Perl init in doc 1.023 2017-09-10 Bug fixes: * add missing backup option to read/write commands (like cme edit, fix, modify ...) * die if both require_config_file and require_backend_argument are set in app file (in Config/Model/[system|user|application].d) 1.022 2017-06-21 Bug fixes for smoke test failures: * test failure exit code to be != 0 * work-around test issue with perl < 5,18 1.021 2017-06-18 Bug fixes: * try to smoke test failures: convert cme test to use App::Cmd::Tester * remove deprecated arg '~~' from doc 1.020 2017-06-05 Improvements for all commands : * allow backend_argument (used by systemd) * unknown options trigger a warning Bug fixes: * all: fix bash completion of -file option * cme run: fix bash_completion of -doc option * cme allow non_upstream_default mode * all: remove -dev option which raises security issues. Use 'perl -Ilib -S cme' for the same effect 1.019 2017-04-30 This release brings some improvements: * run: allow to use command args in var: lines * cme: add '-verbose [ info | debug | trace ]' option to get debug logs Other bug fixes: * do not replace hash variable in run script 1.018 2017-04-07 This release improve 'cme run' command to make it more useful: * add -list option * can compute script variables (var: section) * can use environment variables * add possibility for scritps to provide their own doc displayed with 'cme run xxx -doc' * die when if a script variable is not defined * look for system scripts in /usr/share/ * add bash completion for cme run command Other changes: * save option can now be used with all subcommand 1.017 2017-03-05 This release bring a new cme sub-command to let you create small configuration scripts: cme run Misc: * update © years 1.016 2016-12-12 Bug fix: * check command: propagate -force option to skip load check * dump command must always be in quiet mode 1.015 2016-10-14 Bug fix: * cme: support utf8 in command args (Debian #839593) 1.014 2016-09-24 Improvements: * run deep_check when running commands check, edit, modify, shell (requires Config::Model 2.089) * improved error msg for unknown app (Closes Debian #836794) Bug fix: * cme: fix -strack-trace option 1.013 2016-07-10 Usability improvements: * clarify doc of -strict option (check cmd) * fix typo in pod doc (tx Thomas Schmitt) * suggest 'cme fix' when 'cme check' finds warnings * add CONTRIBUTE.md 1.012 2016-05-29 Bug fix: * edit command degrades gracefully when recommended packages are not isntalled * shell command degrades gracefully without Term::ReadLine 1.011 2016-04-21 New functionality: * dump command can dump any config file(s) in YAML, JSON or Perl format. 1.010 2016-01-30 New functionality: * Can read configuration directory from application file, e.g. user.d/ssh. This will help reuse Model between applications (e.g. systemd and systemd --user). This requires Config::Model 2.077 Bug fix: * cme list: fix -dev option option (Debian #813188) 1.009 2016-01-02 Bug fix release: * remove warning about missing config file. It's better to let the read/write backend handle bogus config file * update command: improved messages shown to user * list command: now work with -dev option 1.008 2015-11-14 Bug fix release: * fix -save option with modify command * fix -try-app-as-model option 1.007 2015-10-25 Usability improvements: * cme: added -file option to replace '~~' argument * bash_completion: added update sub command Doc fixes: * cme: fixed synopsis * cme: added update doc 1.006 2015-07-19 Doc fix release: * cme edit doc: don't abuse L<> tag which are change in "the xxx manpage" * cme list: improve help shown to user * cme help: transform pod to text to avoid showing pod markup * cme fix: corrected small mistakes in doc * cme check: added another example in pod doc 1.005 2015-05-25 * all: fixed confusing unwarranted warning message (about ~~ argument) 1.004 2015-05-25 Mostly a bug fix release. Please use now github to report issues on cme ( https://github.com/dod38fr/cme-perl/issues ) * metadata: * switched bugtracker to github's * fixed repo and website urls (Tx xtaran) * all: add a warning if conf file is not found (may mean a missing ~~ argument). (I'd welcome ideas on how to replace this iffy ~~ argument) * dump command: + added possibility to select a node to dump * fixed -dumptype which is not mandatory * test can run with local cme or system cme to fix debian continuous integrations tests 1.003 2015-04-26 Bug fix release: * Common: pass cme messages through on_message_cb * update: call C::M::Instance->update (require Config::Model 2.068) * remove debug call to YYY 1.002 2015-01-10 * gen-class-pod: * fix doc and missing command arguments * update: + added -edit option to run editor after an update * fix typo in pod doc * dist.ini: updated © to 2015 1.001 2014-11-29 * require Config::Model 2.063 (to avoid bash_completion clash) * improved messages printed by update command * added 'gen-class-pod' alias for gen_class_pod * bash_completion: + added help version commands update in hard-coded list of commands * fixed syntax error 0.001 2014-11-25 First release of App::Cme. These are the changes done to cme compared to the old cme shipped with Config::Model 2.061: * Cme has been re-written with App::Cmd - old mechanism for cme extension is removed. Extensions support is provided by App::Cmd + added gen_class_pod and update commands + added -quiet option to suppress progress messages * fix mistached options wrt available commands in bash_completion * added -root-dir option (used for tests) App-Cme-1.026/lib/0000755000175000017500000000000013216003010012136 5ustar domidomiApp-Cme-1.026/lib/App/0000755000175000017500000000000013216003010012656 5ustar domidomiApp-Cme-1.026/lib/App/Cme.pm0000644000175000017500000000565213216003010013730 0ustar domidomi# # This file is part of App-Cme # # This software is Copyright (c) 2017 by Dominique Dumont. # # This is free software, licensed under: # # The GNU Lesser General Public License, Version 2.1, February 1999 # package App::Cme ; $App::Cme::VERSION = '1.026'; use strict; use warnings; use 5.10.1; # See App::Cmd for explanations on this file use App::Cmd::Setup -app; 1; # ABSTRACT: Check or edit configuration data with Config::Model __END__ =pod =encoding UTF-8 =head1 NAME App::Cme - Check or edit configuration data with Config::Model =head1 VERSION version 1.026 =head1 AUTHOR Dominique Dumont =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2017 by Dominique Dumont. This is free software, licensed under: The GNU Lesser General Public License, Version 2.1, February 1999 =for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan =head1 SUPPORT =head2 Websites The following websites have more information about this module, and may be of help to you. As always, in addition to those websites please use your favorite search engine to discover more resources. =over 4 =item * Search CPAN The default CPAN search engine, useful to view POD in HTML format. L =item * AnnoCPAN The AnnoCPAN is a website that allows community annotations of Perl module documentation. L =item * CPAN Ratings The CPAN Ratings is a website that allows community ratings and reviews of Perl modules. L =item * CPANTS The CPANTS is a website that analyzes the Kwalitee ( code metrics ) of a distribution. L =item * CPAN Testers The CPAN Testers is a network of smoke testers who run automated tests on uploaded CPAN distributions. L =item * CPAN Testers Matrix The CPAN Testers Matrix is a website that provides a visual overview of the test results for a distribution on various Perls/platforms. L =item * CPAN Testers Dependencies The CPAN Testers Dependencies is a website that shows a chart of the test results of all dependencies for a distribution. L =back =head2 Bugs / Feature Requests Please report any bugs or feature requests by email to C, or through the web interface at L. You will be automatically notified of any progress on the request by the system. =head2 Source Code The code is open to the world, and available for you to hack on. Please feel free to browse it and play with it, or whatever. If you want to contribute patches, please send me a diff or prod me to pull from your repository :) L git clone https://github.com/dod38fr/cme-perl.git =cut App-Cme-1.026/lib/App/Cme/0000755000175000017500000000000013216003010013362 5ustar domidomiApp-Cme-1.026/lib/App/Cme/Common.pm0000644000175000017500000002056013216003010015153 0ustar domidomi# # This file is part of App-Cme # # This software is Copyright (c) 2017 by Dominique Dumont. # # This is free software, licensed under: # # The GNU Lesser General Public License, Version 2.1, February 1999 # #ABSTRACT: Common methods for App::Cme package App::Cme::Common; $App::Cme::Common::VERSION = '1.026'; use strict; use warnings; use 5.10.1; use Config::Model 2.116; use Config::Model::Lister; use Pod::POM; use Pod::POM::View::Text; use Scalar::Util qw/blessed/; use Path::Tiny; use Encode qw(decode_utf8); my @store; sub cme_global_options { my ( $class, $app ) = @_; my @global_options = ( [ "model-dir=s" => "Specify an alternate directory to find model files"], [ "try-app-as-model!" => "try to load a model using directly the application name " . "specified as 3rd parameter on the command line"], [ "save!" => "Force a save even if no change was done" ], [ "force-load!" => "Load file even if error are found in data. Bad data are discarded (imply save)"], [ "create!" => "start from scratch."], [ "root-dir=s" => "Change root directory. Mostly used for test"], [ "file=s" => "Specify a target file"], # to be deprecated [ "canonical!" => "write back config data according to canonical order" ], [ "trace|stack-trace!" => "Provides a full stack trace when exiting on error"], [ "verbose=s" => "Verbosity level (1, 2, 3 or info, debug, trace)"], # no bundling { getopt_conf => [ qw/no_bundling/ ] } ); return ( @global_options, ); } sub check_unknown_args { my ($self, $args) = @_; my @unknown_options = grep { /^-/ } @$args ; # $self->usage_error("Unknown option: @unknown_options") if @unknown_options; warn("Unknown option: @unknown_options. Unknown option will soon be a fatal error.") if @unknown_options; } # modifies $args in place sub process_args { my ($self, $opt, $args) = @_; # see Debian #839593 and perlunicook(1) section X 13 @$args = map { decode_utf8($_, 1) } @$args; my ( $categories, $appli_info, $appli_map ) = Config::Model::Lister::available_models; my $application = shift @$args; my $root_model = $appli_map->{$application}; $root_model ||= $application if $opt->{try_app_as_model}; Config::Model::Exception::Any->Trace(1) if $opt->{trace}; if ( not defined $root_model ) { die "Can't locate model for application '$application'.\n" . "Run 'cme list' for the list of models available on your system.\n" . "You may need to install another Config::Model Perl module.\n" . "See the available models there: https://github.com/dod38fr/config-model/wiki/Available-models-and-backends\n"; } say "cme: using $root_model model" unless $opt->{quiet}; my $command = (split('::', ref($self)))[-1] ; if ($appli_info->{$application}{require_config_file} and $appli_info->{$application}{require_backend_argument}) { die "Error in $root_model model: cannot have both require_config_file and require_backend_argument.\n"; } # @ARGV should be [ $config_file ] [ modification_instructions ] my $config_file; if ( $appli_info->{$application}{require_config_file} ) { $config_file = $opt->{file} || shift @$args ; $self->usage_error( "no config file specified. Command should be 'cme $command $application configuration_file'", ) unless $config_file; } elsif ( $appli_info->{$application}{allow_config_file_override}) { $config_file = $opt->{file}; } if ( $appli_info->{$application}{require_backend_argument} ) { # let the backend handle a missing arg and provide a clear error message my $b_arg = $opt->{_backend_arg} = shift @$args ; if (not $b_arg) { my $message = $appli_info->{$application}{backend_argument_info} ; my $insert = $message ? " ( $message )": ''; die "application $application requires a 3rd argument$insert. " . "I.e. 'cme $command $application '\n"; } } # remove legacy '~~' if ($args->[0] and $args->[0] eq '~~') { warn "Argument '~~' was a bad idea and is now ignored. Use -file option to " ."specify a target file or just forget about '~~' argument\n"; shift @$args; } # override (or specify) configuration dir $opt->{_config_dir} = $appli_info->{$application}{config_dir}; $opt->{_application} = $application ; $opt->{_config_file} = $config_file; $opt->{_root_model} = $root_model; } sub model { my ($self, $opt, $args) = @_; my @levels = qw/WARN INFO DEBUG TRACE/; my $optv = $opt->{verbose} ; my $log_level; if (defined $optv) { if ($optv =~ /^\d$/) { $log_level = $levels[$opt->{verbose}]; } else { ($log_level) = grep { /^$optv/i } @levels; } if (not $log_level) { die "unknown log level $optv. Must be 1 ,2, 3 or warn, info, debug, trace.\n" ; } } my %cm_args; $cm_args{model_dir} = $opt->{model_dir} if $opt->{model_dir}; $cm_args{log_level} = $log_level if $log_level; if (not $self->{_model}) { my $model = $self->{_model} = Config::Model->new( %cm_args ); push @store, $model; } return $self->{_model}; } sub instance { my ($self, $opt, $args) = @_; my %instance_args = ( root_class_name => $opt->{_root_model}, instance_name => $opt->{_application}, application => $opt->{_application}, check => $opt->{force_load} ? 'no' : 'yes', auto_create => $opt->{create}, backend_arg => $opt->{_backend_arg}, config_file => $opt->{_config_file}, config_dir => $opt->{_config_dir}, ); foreach my $param (qw/root_dir canonical backup/) { $instance_args{$param} = $opt->{$param} if defined $opt->{$param}; } return $self->{_instance} ||= $self->model->instance(%instance_args); } sub init_cme { my $self = shift; # model and inst are deleted if not kept in a scope return ( $self->model(@_) , $self->instance(@_), $self->instance->config_root ); } sub save { my ($self,$inst,$opt) = @_; $inst->say_changes unless $opt->{quiet}; # if load was forced, must write back to clean up errors (even if they are not changes # at semantic level, i.e. removed unnecessary stuff) $inst->write_back( force => $opt->{force_load} || $opt->{save} ); } sub run_tk_ui { my ($self, $root, $opt) = @_; require Config::Model::TkUI; require Tk; require Tk::ErrorDialog; Tk->import; no warnings 'once'; my $mw = MainWindow->new; $mw->withdraw; # Thanks to Jerome Quelin for the tip $mw->optionAdd( '*BorderWidth' => 1 ); my $cmu = $mw->ConfigModelUI( -root => $root, ); $root->instance->on_message_cb(sub{$cmu->show_message(@_);}); if ($opt->{open_item}) { my $obj = $root->grab($opt->{open_item}); $cmu->force_element_display($obj); } &MainLoop; # Tk's } sub run_shell_ui ($$$) { my ($self, $term_class, $inst) = @_; my $shell_ui = $term_class->new ( root => $inst->config_root, title => $inst->application . ' configuration', prompt => ' >', ); # engage in user interaction $shell_ui->run_loop; } sub get_documentation { my ($self) = @_; my $parser = Pod::POM->new(); my $pkg = blessed ($self); $pkg =~ s!::!/!g; my $pom = $parser->parse_file($INC{$pkg.'.pm'}) || die $parser->error(); my $sections = $pom->head1(); my @ret ; foreach my $s (@$sections) { push (@ret ,$s) if $s->title() =~ /DESCRIPTION|EXIT/; } return join ("", map { Pod::POM::View::Text->print($_)} @ret) . "Options:\n";; } 1; __END__ =pod =encoding UTF-8 =head1 NAME App::Cme::Common - Common methods for App::Cme =head1 VERSION version 1.026 =head1 SYNOPSIS # Internal. Used by App::Cme::Command::* =head1 DESCRIPTION Common methods for all cme commands =head1 AUTHOR Dominique Dumont =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2017 by Dominique Dumont. This is free software, licensed under: The GNU Lesser General Public License, Version 2.1, February 1999 =cut App-Cme-1.026/lib/App/Cme/Command/0000755000175000017500000000000013216003010014740 5ustar domidomiApp-Cme-1.026/lib/App/Cme/Command/run.pm0000644000175000017500000002743713216003010016117 0ustar domidomi# # This file is part of App-Cme # # This software is Copyright (c) 2017 by Dominique Dumont. # # This is free software, licensed under: # # The GNU Lesser General Public License, Version 2.1, February 1999 # # ABSTRACT: Run a cme script package App::Cme::Command::run ; $App::Cme::Command::run::VERSION = '1.026'; use strict; use warnings; use 5.10.1; use File::HomeDir; use Path::Tiny; use Config::Model; use Log::Log4perl qw(get_logger :levels); use Encode qw(decode_utf8); use App::Cme -command ; use base qw/App::Cme::Common/; my $__test_home = ''; sub _set_test_home { $__test_home = shift; } my $home = $__test_home || File::HomeDir->my_home; my @script_paths = map {path($_)} ( "$home/.cme/scripts", "/etc/cme/scripts/", ); push @script_paths, path($INC{"Config/Model.pm"})->parent->child("Model/scripts") ; sub opt_spec { my ( $class, $app ) = @_; return ( [ "arg=s@" => "script argument. run 'cme run $app -doc' for possible arguments" ], [ "backup:s" => "Create a backup of configuration files before saving." ], [ "commit|c:s" => "commit change with passed message" ], [ "cat" => "Show the script file" ], [ "no-commit|nc!" => "skip commit to git" ], [ "quiet!" => "Suppress progress messages" ], [ "doc!" => "show documention of script" ], [ "list!" => "list available scripts" ], $class->cme_global_options, ); } sub validate_args { my ($self, $opt, $args) = @_; $self->check_unknown_args($args); } sub usage_desc { my ($self) = @_; my $desc = $self->SUPER::usage_desc; # "%c COMMAND %o" return "$desc [ script ] [ -args foo=12 [ -args bar=13 ]"; } sub description { my ($self) = @_; return $self->get_documentation; } sub execute { my ($self, $opt, $app_args) = @_; # cannot use logger until Config::Model is initialised # see Debian #839593 and perlunicook(1) section X 13 @$app_args = map { decode_utf8($_, 1) } @$app_args; if ($opt->{list}) { my @scripts; foreach my $path ( @script_paths ) { next unless $path->is_dir; push @scripts, grep { ! /~$/ } $path->children(); } say "Available scripts:"; say map {"- ".$_->basename."\n"} @scripts ; return; } my $script_name = shift @$app_args; my $script; if ($script_name =~ m!/!) { $script = path($script_name); } else { # check script in known locations foreach my $path ( @script_paths ) { next unless $path->is_dir; $script = $path->child($script_name); last if $script->is_file; } } die "Error: cannot find script $script_name\n" unless $script->is_file; my $content = $script->slurp_utf8; if ($opt->{cat}) { print $content; return; } # parse variables passed on command line my %user_args = map { split '=',$_,2; } @{ $opt->{arg} }; if ($content =~ m/^#!/ or $content =~ /^use/m) { splice @ARGV, 0,2; # remove 'run script' arguments eval $script->slurp_utf8; die "Error in script $script_name: $@\n" if $@; return; } my %var; # find if all variables are accounted for my %missing ; # provide default values my %default ; # %args can be used in var section of a script. A new entry in # added in %missing if the script tries to read an undefined value tie my %args, 'App::Cme::Run::Var', \%missing, \%default; %args = %user_args; # replace variables with command arguments or eval'ed variables or env variables my $replace_var = sub { # change $var but not \$var $_[0] =~ s~ (?($value) unless $key eq 'var'; if ($key =~ /^app/) { unshift @$app_args, $value; } elsif ($key eq 'var') { my $res = eval ($value) ; die "Error in var specification line $line_nb: $@\n" if $@; } elsif ($key eq 'default') { my ($dk, $dv) = split /[\s:=]+/, $value, 2; $default{$dk} = $dv; } elsif ($key eq 'doc') { push @doc, $value; } elsif ($key eq 'load') { push @load, $value; } elsif ($key eq 'commit') { $commit_msg = $value; } else { die "Error in file $script line $line_nb: unexpected '$key' instruction\n"; } } if ($opt->doc) { say join "\n", @doc; say "will commit with message: '$commit_msg'" if $commit_msg; return; } if (my @missing = sort keys %missing) { die "Error: Missing variables '". join("', '",@missing)."' in command arguments for script $script\n" ."Please use option '".join(' ', map { "-arg $_=xxx"} @missing)."'\n"; } $self->process_args($opt, $app_args); # override commit message. may also trigger a commit even if none # is specified in script $commit_msg ||= $opt->{commit}; # check if workspace and index are clean if ($commit_msg) { my $r = `git status --porcelain`; die "Cannot run commit command in a non clean repo. Please commit or stash pending changes: $r" if $r; } # call loads my ($model, $inst, $root) = $self->init_cme($opt,$app_args); map { $root->load($_) } @load; $self->save($inst,$opt) ; # commit if needed if ($commit_msg and not $opt->{'no-commit'}) { system(qw/git commit -a -m/, $commit_msg); } } package App::Cme::Run::Var; $App::Cme::Run::Var::VERSION = '1.026'; require Tie::Hash; our @ISA = qw(Tie::ExtraHash); sub FETCH { my ($self, $key) = @_ ; my ($h, $missing, $default) = @$self; my $res = $h->{$key} // $default->{$key} ; $missing->{$key} = 1 unless defined $res; return $res // ''; } 1; __END__ =pod =encoding UTF-8 =head1 NAME App::Cme::Command::run - Run a cme script =head1 VERSION version 1.026 =head1 SYNOPSIS $ cat ~/.cme/scripts/remove-mia doc: remove mia from Uploders. Require mia parameter # declare app to configure app: dpkg # specify one or more instructions load: ! control source Uploaders:-~/$mia$/ # commit the modifications with a message (git only) commit: remove MIA dev $mia $ cme run remove-mia -arg mia=longgone@d3bian.org # cme run can also use environment variables $ cat ~/.cme/scripts/add-me-to-uploaders app: dpkg-control load: source Uploaders:.push("$DEBFULLNAME <$DEBEMAIL>") $ cme run add-me-to-uploaders Reading package lists... Done Building dependency tree Reading state information... Done Changes applied to dpkg-control configuration: - source Uploaders:3: '' -> 'Dominique Dumont ' # show the script documentation $ cme run remove-mia -doc remove mia from Uploders. require mia parameter # list scripts $ cme run -list Available scripts: - update-copyright - add-me-to-uploaders =head1 DESCRIPTION Run a script written with cme DSL (Design specific language) or in plain Perl. A script passed by name is searched in C<~/.cme/scripts>, C or C. E.g. with C, C loads either C<~/.cme/scripts/foo>, C or C No search is done if the script is passed with a path (e.g. C) C can also run plain Perl script. This is syntactic sugar to avoid polluting global namespace, i.e. there's no need to store a script using L in C. When run, this script: =over =item * opens the configuration file of C =item * applies the modifications specified with C instructions =item * save the configuration files =item * commits the result if C is specified (either in script or on command line). =back See L for details. =head1 Syntax The script accepts instructions in the form: key: value The script accepts the following instructions: =over =item app Specify the target application. Must be one of the application listed by C command. Mandatory. Only one C instruction is allowed. =item var Use Perl code to specify variables usable in this script. The Perl code must store data in C<%var> hash. For instance: var: my @l = localtime; $var{year} = $l[5]+1900; The hash C<%args> contains the variables passed with the C<-arg> option. Reading a value from C<%args> which is not set by user triggers a missing option error. Use C if you need to test if a argument was set by user: var: $var{foo} = exists $var{bar} ? $var{bar} : 'default' # good var: $var{foo} = $var{bar} || 'default' # triggers a "missing arg" error =item load Specify the modifications to apply using a string as specified in L. This string can contain variable (e.g. C<$foo>) which are replaced by command argument (e.g. C<-arg foo=bar>) or by a variable set in var: line (e.g. C<$var{foo}> as set above) or by an environment variable (e.g. C<$ENV{foo}>) =item commit Specify that the change must be committed with the passed commit message. When this option is used, C raises an error if used on a non-clean workspace. This option works only with L. =back All instructions can use variables like C<$stuff> whose value can be specified with C<-arg> options, with a Perl variable (from C section explained above) or with an environment variable: For instance: cme run -arg var1=foo -arg var2=bar transforms the instruction: load: ! a=$var1 b=$var2 in load: ! a=foo b=bar =head1 Options =head2 list List available scripts and exits. =head2 arg Arguments for the cme scripts which are used to substitute variables. =head2 doc Show the script documentation. (Note that C<--help> options show the documentation of C command) =head2 cat Pop the hood and show the content of the script. =head2 commit Like the commit instruction in script. Specify that the change must be committed with the passed commit message. =head2 no-commit Don't commit to git (even if the above option is set) =head1 Common options See L. =head1 Examples =head2 update copyright years in C $ cat ~/.cme/scripts/update-copyright app: dpkg-copyright load: Files:~ Copyright=~"s/2016,?\s+$name/2017, $name/g" commit: updated copyright year of $name $ cme run update-copyright -arg "name=Dominique Dumont" cme: using Dpkg::Copyright model Changes applied to dpkg-copyright configuration: - Files:"*" Copyright: '2005-2016, Dominique Dumont ' -> '2005-2017, Dominique Dumont ' - Files:"lib/Dpkg/Copyright/Scanner.pm" Copyright: @@ -1,2 +1,2 @@ -2014-2016, Dominique Dumont +2014-2017, Dominique Dumont 2005-2012, Jonas Smedegaard [master ac2e6410] updated copyright year of Dominique Dumont 1 file changed, 2 insertions(+), 2 deletions(-) =head1 SEE ALSO L =head1 AUTHOR Dominique Dumont =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2017 by Dominique Dumont. This is free software, licensed under: The GNU Lesser General Public License, Version 2.1, February 1999 =cut App-Cme-1.026/lib/App/Cme/Command/update.pm0000644000175000017500000000525513216003010016567 0ustar domidomi# # This file is part of App-Cme # # This software is Copyright (c) 2017 by Dominique Dumont. # # This is free software, licensed under: # # The GNU Lesser General Public License, Version 2.1, February 1999 # # ABSTRACT: Update the configuration of an application package App::Cme::Command::update ; $App::Cme::Command::update::VERSION = '1.026'; use strict; use warnings; use 5.10.1; use App::Cme -command ; use base qw/App::Cme::Common/; use Config::Model::ObjTreeScanner; use Config::Model; sub validate_args { my ($self, $opt, $args) = @_; $self->check_unknown_args($args); $self->process_args($opt,$args); } sub opt_spec { my ( $class, $app ) = @_; return ( [ "edit!" => "Run editor after update is done" ], [ "backup:s" => "Create a backup of configuration files before saving." ], [ "quiet!" => "Suppress progress messages" ], $class->cme_global_options, ); } sub usage_desc { my ($self) = @_; my $desc = $self->SUPER::usage_desc; # "%c COMMAND %o" return "$desc [application] [file ]"; } sub description { my ($self) = @_; return $self->get_documentation; } sub execute { my ($self, $opt, $args) = @_; my ( $inst) = $self->instance($opt,$args); say "updating data" unless $opt->{quiet}; my @msgs = $inst->update(quiet => $opt->{quiet}); if (@msgs and not $opt->{quiet}) { say "update done"; } elsif (not $opt->{quiet}) { say "command done, but ".$opt->{_application} . " model has no provision for update"; } if ($opt->{edit}) { say join("\n", grep {defined $_} @msgs ); $self->run_tk_ui ( $inst->config_root, $opt); } else { $self->save($inst,$opt) ; say join("\n", grep {defined $_} @msgs ); } } 1; __END__ =pod =encoding UTF-8 =head1 NAME App::Cme::Command::update - Update the configuration of an application =head1 VERSION version 1.026 =head1 SYNOPSIS cme update dpkg-copyright =head1 DESCRIPTION Update a configuration file. The update is done scanning external resource. For instance, the update of dpkg-copyright is done by scanning the headers of source files. (Actually, only dpkg-copyright model currently supports updates) Example: cme update dpkg-copyright =head1 Common options See L. =head1 options =over =item -open-item Open a specific item of the configuration when opening the editor =back =head1 SEE ALSO L =head1 AUTHOR Dominique Dumont =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2017 by Dominique Dumont. This is free software, licensed under: The GNU Lesser General Public License, Version 2.1, February 1999 =cut App-Cme-1.026/lib/App/Cme/Command/fusefs.pm0000644000175000017500000000675613216003010016607 0ustar domidomi# # This file is part of App-Cme # # This software is Copyright (c) 2017 by Dominique Dumont. # # This is free software, licensed under: # # The GNU Lesser General Public License, Version 2.1, February 1999 # # ABSTRACT: Edit the configuration of an application with fuse package App::Cme::Command::fusefs ; $App::Cme::Command::fusefs::VERSION = '1.026'; use strict; use warnings; use 5.10.1; use App::Cme -command ; use base qw/App::Cme::Common/; use Config::Model::ObjTreeScanner; sub validate_args { my ($self, $opt, $args) = @_; $self->check_unknown_args($args); $self->process_args($opt,$args); eval { require Config::Model::FuseUI; }; my $has_fuse = $@ ? 0 : 1; die "could not load Config::Model::FuseUI. Is Fuse installed ?\n" unless $has_fuse; my $fd = $opt->{fuse_dir}; die "Directory $fd does not exists\n" unless -d $fd; } sub opt_spec { my ( $class, $app ) = @_; return ( [ "fuse-dir=s" => "Directory where the virtual file system will be mounted", {required => 1} ], [ "dfuse!" => "debug fuse problems" ], [ "dir-char=s" => "string to replace '/' in configuration parameter names"], [ "quiet!" => "Suppress progress messages" ], [ "backup:s" => "Create a backup of configuration files before saving." ], $class->cme_global_options, ); } sub usage_desc { my ($self) = @_; my $desc = $self->SUPER::usage_desc; # "%c COMMAND %o" return "$desc [application] [file ] -fuse-dir xxx [ -dir-char x ] "; } sub description { my ($self) = @_; return $self->get_documentation; } sub execute { my ($self, $opt, $args) = @_; my ($model, $inst, $root) = $self->init_cme($opt,$args); my @extra; if (my $dc = $opt->{dir_char}) { push @extra, dir_char_mockup => $dc; } my $fuse_dir = $opt->{fuse_dir}; print "Mounting config on $fuse_dir in background.\n", "Use command 'fusermount -u $fuse_dir' to unmount\n"; my $ui = Config::Model::FuseUI->new( root => $root, mountpoint => $fuse_dir, @extra, ); # now fork my $pid = fork; if ( defined $pid and $pid == 0 ) { # child process, just run fuse and wait for exit $ui->run_loop( debug => $opt->{fuse_debug} ); $self->save($inst,$opt); } # parent process simply exits } 1; __END__ =pod =encoding UTF-8 =head1 NAME App::Cme::Command::fusefs - Edit the configuration of an application with fuse =head1 VERSION version 1.026 =head1 SYNOPSIS =head1 DESCRIPTION Map the configuration file content to a FUSE virtual file system on a directory specified with option C<-fuse-dir>. Modifications done in the fuse file system are saved to the configuration file when C<< fusermount -u >> is run. =head1 Common options See L. =head1 options =over =item -quiet Suppress progress messages. =item -fuse-dir Mandatory. Directory where the virtual file system will be mounted. =item -dfuse Use this option to debug fuse problems. =item -dir-char Fuse will fail if an element name or key name contains '/'. You can specify a subsitution string to replace '/' in the fused dir. Default is C<< >>. =back =head1 SEE ALSO L =head1 AUTHOR Dominique Dumont =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2017 by Dominique Dumont. This is free software, licensed under: The GNU Lesser General Public License, Version 2.1, February 1999 =cut App-Cme-1.026/lib/App/Cme/Command/list.pm0000644000175000017500000000403113216003010016247 0ustar domidomi# # This file is part of App-Cme # # This software is Copyright (c) 2017 by Dominique Dumont. # # This is free software, licensed under: # # The GNU Lesser General Public License, Version 2.1, February 1999 # # ABSTRACT: List applications handled by cme package App::Cme::Command::list ; $App::Cme::Command::list::VERSION = '1.026'; use strict; use warnings; use 5.10.1; use App::Cme -command ; use Config::Model::Lister; sub description { return << "EOD" Show a list all applications where a model is available. This list depends on installed Config::Model modules. Applications are divided in 3 categories: - system: for system wide applications (e.g. daemon like sshd) - user: for user applications (e.g. ssh configuration) - application: misc application like multistrap or Debian packaging EOD } sub opt_spec { my ( $class, $app ) = @_; return ( [ "dev!" => "list includes a model under development"], ); } my %help = ( system => "system configuration files. Use sudo to run cme", user => "user configuration files", application => "miscellaneous application configuration", ); sub execute { my ($self, $opt, $args) = @_; my ( $categories, $appli_info, $appli_map ) = Config::Model::Lister::available_models($opt->dev()); foreach my $cat ( qw/system user application/ ) { my $names = $categories->{$cat} || []; next unless @$names; print $cat," ( ",$help{$cat}," ):\n ", join( "\n ", @$names ), "\n"; } } 1; __END__ =pod =encoding UTF-8 =head1 NAME App::Cme::Command::list - List applications handled by cme =head1 VERSION version 1.026 =head1 SYNOPSIS cme list =head1 DESCRIPTION Show a list all applications where a model is available. This list depends on installed Config::Model modules. =head1 SEE ALSO L =head1 AUTHOR Dominique Dumont =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2017 by Dominique Dumont. This is free software, licensed under: The GNU Lesser General Public License, Version 2.1, February 1999 =cut App-Cme-1.026/lib/App/Cme/Command/fix.pm0000644000175000017500000000614413216003010016071 0ustar domidomi# # This file is part of App-Cme # # This software is Copyright (c) 2017 by Dominique Dumont. # # This is free software, licensed under: # # The GNU Lesser General Public License, Version 2.1, February 1999 # # ABSTRACT: Fix the configuration of an application package App::Cme::Command::fix ; $App::Cme::Command::fix::VERSION = '1.026'; use strict; use warnings; use 5.10.1; use App::Cme -command ; use base qw/App::Cme::Common/; use Config::Model::ObjTreeScanner; sub validate_args { my ($self, $opt, $args) = @_; $self->check_unknown_args($args); $self->process_args($opt,$args); } sub opt_spec { my ( $class, $app ) = @_; return ( [ "from=s@" => "fix only a subset of a configuration tree" ], [ "backup:s" => "Create a backup of configuration files before saving." ], [ "filter=s" => "pattern to select the element name to be fixed"], [ "quiet!" => "Suppress progress messages" ], $class->cme_global_options, ); } sub usage_desc { my ($self) = @_; my $desc = $self->SUPER::usage_desc; # "%c COMMAND %o" return "$desc [application] [ file ]"; } sub description { my ($self) = @_; return $self->get_documentation; } sub execute { my ($self, $opt, $args) = @_; my ($model, $inst, $root) = $self->init_cme($opt,$args); my @fix_from = $opt->{from} ? @{$opt->{from}} : ('') ; foreach my $path (@fix_from) { my $node_to_fix = $inst->config_root->grab($path); my $msg = "cme: running fix on ".$inst->name." configuration"; $msg .= "from node", $node_to_fix->name if $path; say $msg. "..." unless $opt->{quiet}; $node_to_fix->apply_fixes($opt->{fix_filter}); } $self->save($inst,$opt) ; } 1; __END__ =pod =encoding UTF-8 =head1 NAME App::Cme::Command::fix - Fix the configuration of an application =head1 VERSION version 1.026 =head1 SYNOPSIS # fix dpkg (this example requires Config::Model::Dpkg) cme fix dpkg =head1 DESCRIPTION Checks the content of the configuration file of an application (and show warnings if needed), update deprecated parameters (old value are saved to new parameters) and fix warnings are fixed. The configuration is saved if anything was changed. If no changes are done, the file is not saved. =head1 Common options See L. =head1 options =over =item from Use option C<-from> to fix only a subset of a configuration tree. Example: cme fix dpkg -from 'control binary:foo Depends' This option can be repeated: cme fix dpkg -from 'control binary:foo Depends' -from 'control source Build-Depends' =item filter Filter the leaf according to a pattern. The pattern is applied to the element name to be fixed Example: cme fix dpkg -from control -filter Build # will fix all Build-Depends and Build-Depend-Indep or cme fix dpkg -filter Depend =back =head1 SEE ALSO L, L =head1 AUTHOR Dominique Dumont =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2017 by Dominique Dumont. This is free software, licensed under: The GNU Lesser General Public License, Version 2.1, February 1999 =cut App-Cme-1.026/lib/App/Cme/Command/migrate.pm0000644000175000017500000000407513216003010016734 0ustar domidomi# # This file is part of App-Cme # # This software is Copyright (c) 2017 by Dominique Dumont. # # This is free software, licensed under: # # The GNU Lesser General Public License, Version 2.1, February 1999 # # ABSTRACT: Migrate the configuration of an application package App::Cme::Command::migrate ; $App::Cme::Command::migrate::VERSION = '1.026'; use strict; use warnings; use 5.10.1; use App::Cme -command ; use base qw/App::Cme::Common/; use Config::Model::ObjTreeScanner; sub validate_args { my ($self, $opt, $args) = @_; $self->check_unknown_args($args); $self->process_args($opt,$args); } sub opt_spec { my ( $class, $app ) = @_; return ( [ "backup:s" => "Create a backup of configuration files before saving." ], [ "quiet!" => "Suppress progress messages" ], $class->cme_global_options, ); } sub usage_desc { my ($self) = @_; my $desc = $self->SUPER::usage_desc; # "%c COMMAND %o" return "$desc [application] [file ]"; } sub description { my ($self) = @_; return $self->get_documentation; } sub execute { my ($self, $opt, $args) = @_; my ($model, $inst, $root) = $self->init_cme($opt,$args); $root->migrate; $self->save($inst,$opt) ; } 1; __END__ =pod =encoding UTF-8 =head1 NAME App::Cme::Command::migrate - Migrate the configuration of an application =head1 VERSION version 1.026 =head1 SYNOPSIS # check dpkg files, update deprecated parameters and save cme migrate dpkg =head1 DESCRIPTION Checks the content of the configuration file of an application (and show warnings if needed), update deprecated parameters (old value are saved to new parameters) and save the new configuration. See L. For more details, see L =head1 Common options See L. =head1 SEE ALSO L =head1 AUTHOR Dominique Dumont =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2017 by Dominique Dumont. This is free software, licensed under: The GNU Lesser General Public License, Version 2.1, February 1999 =cut App-Cme-1.026/lib/App/Cme/Command/modify.pm0000644000175000017500000000536113216003010016572 0ustar domidomi# # This file is part of App-Cme # # This software is Copyright (c) 2017 by Dominique Dumont. # # This is free software, licensed under: # # The GNU Lesser General Public License, Version 2.1, February 1999 # # ABSTRACT: Modify the configuration of an application package App::Cme::Command::modify ; $App::Cme::Command::modify::VERSION = '1.026'; use strict; use warnings; use 5.10.1; use App::Cme -command ; use base qw/App::Cme::Common/; use Config::Model::ObjTreeScanner; sub validate_args { my ($self, $opt, $args) = @_; $self->check_unknown_args($args); $self->process_args($opt,$args); $self->usage_error("No modification instructions given on command line") unless @$args or $opt->{save}; } sub opt_spec { my ( $class, $app ) = @_; return ( [ "backup:s" => "Create a backup of configuration files before saving." ], [ "quiet!" => "Suppress progress messages" ], $class->cme_global_options, ); } sub usage_desc { my ($self) = @_; my $desc = $self->SUPER::usage_desc; # "%c COMMAND %o" return "$desc [application] [file ] instructions"; } sub description { my ($self) = @_; return $self->get_documentation; } sub execute { my ($self, $opt, $args) = @_; my ($model, $inst, $root) = $self->init_cme($opt,$args); # needed to create write_back subs $root->dump_tree() if $opt->{save} and not @$args; $root->load("@$args"); $root->deep_check; # consistency check $self->save($inst,$opt) ; } 1; __END__ =pod =encoding UTF-8 =head1 NAME App::Cme::Command::modify - Modify the configuration of an application =head1 VERSION version 1.026 =head1 SYNOPSIS # modify configuration with command line cme modify dpkg source 'format="(3.0) quilt"' =head1 DESCRIPTION Modify a configuration file with the values passed on the command line. These command must follow the syntax defined in L (which is similar to the output of L command) Example: cme modify dpkg 'source format="(3.0) quilt"' cme modify multistrap my_mstrap.conf 'sections:base source="http://ftp.fr.debian.org"' Some application like dpkg-copyright allows you to override the configuration file name. You must then use C<-file> option: cme modify dpkg-copyright -file ubuntu/copyright 'Comment="Silly example"' =head1 Common options See L. =head1 options =over =item -save Force a save even if no change was done. Useful to reformat the configuration file. =back =head1 SEE ALSO L =head1 AUTHOR Dominique Dumont =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2017 by Dominique Dumont. This is free software, licensed under: The GNU Lesser General Public License, Version 2.1, February 1999 =cut App-Cme-1.026/lib/App/Cme/Command/check.pm0000644000175000017500000000632313216003010016357 0ustar domidomi# # This file is part of App-Cme # # This software is Copyright (c) 2017 by Dominique Dumont. # # This is free software, licensed under: # # The GNU Lesser General Public License, Version 2.1, February 1999 # # ABSTRACT: Check the configuration of an application package App::Cme::Command::check ; $App::Cme::Command::check::VERSION = '1.026'; use strict; use warnings; use 5.10.1; use App::Cme -command ; use base qw/App::Cme::Common/; use Config::Model::ObjTreeScanner; sub validate_args { my ($self, $opt, $args) = @_; $self->check_unknown_args($args); $self->process_args($opt,$args); } sub opt_spec { my ( $class, $app ) = @_; return ( [ "strict!" => "cme will exit 1 if warnings are found during check" ], [ "quiet!" => "Suppress progress messages" ], $class->cme_global_options, ); } sub usage_desc { my ($self) = @_; my $desc = $self->SUPER::usage_desc; # "%c COMMAND %o" return "$desc [application] [ config_file ]"; } sub description { my ($self) = @_; return $self->get_documentation; } sub execute { my ($self, $opt, $args) = @_; my ($model, $inst, $root) = $self->init_cme($opt,$args); my $check = $opt->{force_load} ? 'no' : 'yes' ; say "loading data" unless $opt->{quiet}; Config::Model::ObjTreeScanner->new( leaf_cb => sub { }, check => $check, )->scan_node( undef, $root ); say "checking data" unless $opt->{quiet}; $root->dump_tree( mode => 'full' ); # light check (value per value) $root->deep_check; # consistency check say "check done" unless $opt->{quiet}; my $ouch = $inst->has_warning; if ( $ouch ) { my $app = $inst->application; warn "you can try 'cme fix $app' to fix the warnings shown above\n"; die "Found $ouch warnings in strict mode\n" if $opt->{strict}; } } 1; __END__ =pod =encoding UTF-8 =head1 NAME App::Cme::Command::check - Check the configuration of an application =head1 VERSION version 1.026 =head1 SYNOPSIS # standard usage cme check popcon # read data from arbitrary file (with Config::Model::Dpkg) cme check dpkg-copyright path/to/file =head1 DESCRIPTION Checks the content of the configuration file of an application. Prints warnings and errors on STDOUT. Example: cme check fstab Some applications allows one to override the default configuration file. For instance, with Debian copyright model, you can run cme on a different file: cme check dpkg-copyright foobar or directly check copyright data on STDIN: curl http://metadata.ftp-master.debian.org/changelogs/main/f/frozen-bubble/unstable_copyright \ | cme check dpkg-copyright - =head1 Common options See L. =head1 options =over =item -strict When set, cme exits 1 if warnings are found. By default, C exits 0 when warnings are found. =back =head1 EXIT CODE cme exits 0 when no errors are found. Exit 1 otherwise. If C<-strict> option is set, cme exits 1 when warnings are found. =head1 SEE ALSO L =head1 AUTHOR Dominique Dumont =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2017 by Dominique Dumont. This is free software, licensed under: The GNU Lesser General Public License, Version 2.1, February 1999 =cut App-Cme-1.026/lib/App/Cme/Command/shell.pm0000644000175000017500000000475713216003010016422 0ustar domidomi# # This file is part of App-Cme # # This software is Copyright (c) 2017 by Dominique Dumont. # # This is free software, licensed under: # # The GNU Lesser General Public License, Version 2.1, February 1999 # # ABSTRACT: Edit the configuration of an application with a shell package App::Cme::Command::shell ; $App::Cme::Command::shell::VERSION = '1.026'; use strict; use warnings; use 5.10.1; use App::Cme -command ; use base qw/App::Cme::Common/; sub validate_args { my ($self, $opt, $args) = @_; $self->check_unknown_args($args); $self->process_args($opt,$args); } sub opt_spec { my ( $class, $app ) = @_; return ( [ "open-item=s" => "open a specific item of the configuration" ], [ "backup:s" => "Create a backup of configuration files before saving." ], [ "quiet!" => "Suppress progress messages" ], [ "bare!" => "run bare terminal UI"], $class->cme_global_options, ); } sub usage_desc { my ($self) = @_; my $desc = $self->SUPER::usage_desc; # "%c COMMAND %o" return "$desc [application] [file ]"; } sub description { my ($self) = @_; return $self->get_documentation; } sub execute { my ($self, $opt, $args) = @_; my ($model, $inst, $root) = $self->init_cme($opt,$args); $root->deep_check; if ($opt->{bare}) { require Config::Model::SimpleUI; $self->run_shell_ui('Config::Model::SimpleUI', $inst) ; } else { require Config::Model::TermUI; $self->run_shell_ui('Config::Model::TermUI', $inst) ; } } 1; __END__ =pod =encoding UTF-8 =head1 NAME App::Cme::Command::shell - Edit the configuration of an application with a shell =head1 VERSION version 1.026 =head1 SYNOPSIS # simple shell like interface cme shell dpkg-copyright =head1 DESCRIPTION Edit the configuration with a shell like interface. See L for details. This is a shortcut for C. See L. =head1 Common options See L. =head1 options =over =item -open-item Open a specific item of the configuration when opening the editor =item -quiet Suppress porgress message =item -bare Use Term UI without auto-completion or font enhancements. =back =head1 SEE ALSO L =head1 AUTHOR Dominique Dumont =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2017 by Dominique Dumont. This is free software, licensed under: The GNU Lesser General Public License, Version 2.1, February 1999 =cut App-Cme-1.026/lib/App/Cme/Command/edit.pm0000644000175000017500000001105313216003010016223 0ustar domidomi# # This file is part of App-Cme # # This software is Copyright (c) 2017 by Dominique Dumont. # # This is free software, licensed under: # # The GNU Lesser General Public License, Version 2.1, February 1999 # # ABSTRACT: Edit the configuration of an application package App::Cme::Command::edit ; $App::Cme::Command::edit::VERSION = '1.026'; use strict; use warnings; use 5.10.1; use App::Cme -command ; use base qw/App::Cme::Common/; use Config::Model::ObjTreeScanner; sub validate_args { my ($self, $opt, $args) = @_; $self->check_unknown_args($args); $self->process_args($opt,$args); } sub opt_spec { my ( $class, $app ) = @_; return ( [ "ui|if=s" => "user interface type. Either tk, curses, shell" ], [ "backup:s" => "Create a backup of configuration files before saving." ], [ "open-item=s" => "open a specific item of the configuration" ], [ "quiet!" => "Suppress progress messages" ], $class->cme_global_options, ); } sub usage_desc { my ($self) = @_; my $desc = $self->SUPER::usage_desc; # "%c COMMAND %o" return "$desc [application] [ file ] [ -ui tk|curses|shell ] [ -open-item xxx ] "; } sub description { my ($self) = @_; return $self->get_documentation; } sub execute { my ($self, $opt, $args) = @_; my ($model, $inst, $root) = $self->init_cme($opt,$args); eval { require Config::Model::TkUI; }; my $has_tk = $@ ? 0 : 1; eval { require Config::Model::CursesUI; }; my $has_curses = $@ ? 0 : 1; my $ui_type = $opt->{ui}; if ( not defined $ui_type ) { if ($has_tk) { $ui_type = 'tk'; } elsif ($has_curses) { warn "You should install Config::Model::TkUI for a ", "more friendly user interface\n"; $ui_type = 'curses'; } else { warn "You should install Config::Model::TkUI or ", "Config::Model::CursesUI ", "for a more friendly user interface\n"; $ui_type = 'shell'; } } $root->deep_check; if ( $ui_type eq 'shell' ) { require Config::Model::TermUI; $self->run_shell_ui('Config::Model::TermUI', $inst) ; } elsif ( $ui_type eq 'curses' ) { die "cannot run curses interface: ", "Config::Model::CursesUI is not installed, please use shell or simple UI\n" unless $has_curses; my $err_file = '/tmp/cme-error.log'; print "In case of error, check $err_file\n"; open( FH, "> $err_file" ) || die "Can't open $err_file: $!"; open STDERR, ">&FH"; my $dialog = Config::Model::CursesUI->new(); # engage in user interaction $dialog->start($model); close FH; } elsif ( $ui_type eq 'tk' ) { die "cannot run Tk interface: Config::Model::TkUI is not installed, please use curses or shell or simple ui\n" unless $has_tk; $self ->run_tk_ui ( $root, $opt); } else { die "Unsupported user interface: $ui_type"; } } 1; __END__ =pod =encoding UTF-8 =head1 NAME App::Cme::Command::edit - Edit the configuration of an application =head1 VERSION version 1.026 =head1 SYNOPSIS # edit dpkg config with GUI (requires Config::Model::Dpkg) cme edit dpkg # force usage of simple shell like interface cme edit dpkg-copyright --ui shell # edit /etc/sshd_config (requires Config::Model::OpenSsh) sudo cme edit sshd # edit ~/.ssh/config (requires Config::Model::OpenSsh) cme edit ssh # edit a file (file name specification is mandatory here) cme edit multistrap my.conf =head1 DESCRIPTION Edit a configuration. By default, a Tk GUI will be opened if C is installed. You can choose another user interface with the C<-ui> option: =over =item * C: provides a Tk graphical interface (If C is installed). =item * C: provides a curses user interface (If L is installed). =item * C: provides a shell like interface. See L for details. This is equivalent to running C command. =back =head1 Common options See L. =head1 options =over =item -open-item Open a specific item of the configuration when opening the editor =item -quiet Suppress porgress message =back =head1 SEE ALSO L =head1 AUTHOR Dominique Dumont =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2017 by Dominique Dumont. This is free software, licensed under: The GNU Lesser General Public License, Version 2.1, February 1999 =cut App-Cme-1.026/lib/App/Cme/Command/gen_class_pod.pm0000644000175000017500000000317213216003010020101 0ustar domidomi# # This file is part of App-Cme # # This software is Copyright (c) 2017 by Dominique Dumont. # # This is free software, licensed under: # # The GNU Lesser General Public License, Version 2.1, February 1999 # # ABSTRACT: Generates pod doc from model files package App::Cme::Command::gen_class_pod ; $App::Cme::Command::gen_class_pod::VERSION = '1.026'; use strict; use warnings; use 5.10.1; use App::Cme -command ; use Config::Model::Utils::GenClassPod; sub command_names { my $self = shift ; return ( 'gen-class-pod' , $self->SUPER::command_names ); } sub description { return << "EOD" Generate pod documentation from configuration models found in ./lib directory EOD } sub execute { my ($self, $opt, $args) = @_; gen_class_pod(@$args); } 1; __END__ =pod =encoding UTF-8 =head1 NAME App::Cme::Command::gen_class_pod - Generates pod doc from model files =head1 VERSION version 1.026 =head1 SYNOPSIS cme gen-class-pod [ Foo ... ] =head1 DESCRIPTION This command scans C<./lib/Config/Model/models/*.d> and generate pod documentation for each file found there using L You can also pass one or more class names. C will write the documentation for each passed class and all other classes used by the passed classes. =head1 SEE ALSO L, L =head1 AUTHOR Dominique Dumont =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2017 by Dominique Dumont. This is free software, licensed under: The GNU Lesser General Public License, Version 2.1, February 1999 =cut App-Cme-1.026/lib/App/Cme/Command/dump.pm0000644000175000017500000000712213216003010016245 0ustar domidomi# # This file is part of App-Cme # # This software is Copyright (c) 2017 by Dominique Dumont. # # This is free software, licensed under: # # The GNU Lesser General Public License, Version 2.1, February 1999 # # ABSTRACT: Dump the configuration of an application package App::Cme::Command::dump ; $App::Cme::Command::dump::VERSION = '1.026'; use strict; use warnings; use 5.10.1; use App::Cme -command ; use base qw/App::Cme::Common/; use Config::Model::ObjTreeScanner; use YAML; use JSON; use Data::Dumper; sub validate_args { my ($self, $opt, $args) = @_; $self->check_unknown_args($args); $opt->{quiet} = 1; # don't want to mess up yaml output $self->process_args($opt,$args); } sub opt_spec { my ( $class, $app ) = @_; return ( [ "dumptype=s" => "Dump all values (full) or only customized values", { regex => qr/^(?:full|custom|non_upstream_default)$/, default => 'custom' } ], [ "format=s" => "dump using specified format", { regex => qr/^(?:json|ya?ml|perl|cml|cds)$/i, default => 'yaml' }, ], [ "quiet!" => "Suppress progress messages" ], $class->cme_global_options, ); } sub usage_desc { my ($self) = @_; my $desc = $self->SUPER::usage_desc; # "%c COMMAND %o" return "$desc [application] [ config_file ] [ -dumptype full|custom ] [ path ]"; } sub description { my ($self) = @_; return $self->get_documentation; } sub execute { my ($self, $opt, $args) = @_; my ($model, $inst, $root) = $self->init_cme($opt,$args); my $target_node = $root->grab(step => "@$args", type => 'node'); my $dump_string; my $format = $opt->{format}; my $mode = $opt->{dumptype} || 'custom'; if ($format =~ /cml|cds/i) { $dump_string = $target_node->dump_tree( mode => $mode ); } else { my $perl_data = $target_node->dump_as_data( ordered_hash_as_list => 0, mode => $mode ); $dump_string = $format =~ /ya?ml/i ? Dump($perl_data) : $format =~ /json/i ? encode_json($perl_data) : Dumper($perl_data) ; # Perl data structure } print $dump_string ; } 1; __END__ =pod =encoding UTF-8 =head1 NAME App::Cme::Command::dump - Dump the configuration of an application =head1 VERSION version 1.026 =head1 SYNOPSIS # dump ~/.ssh/config in cme syntax # (this example requires Config::Model::OpenSsh) $ cme dump -format cml ssh Host:"*" - Host:"*.debian.org" User=dod - =head1 DESCRIPTION Dump configuration content on STDOUT with YAML format. By default, dump only custom values, i.e. different from application built-in values or model default values. You can use the C<-dumptype> option for other types of dump: -dumptype [ full | custom | non_upstream_default ] Choose to dump every values (full), or only customized values (default) C is like C mode, but value identical with application default are omitted. But this should seldom happen. By default, dump in yaml format. This can be changed in C, C, C (aka L format, C is also accepted) with C<-format> option. =head1 Common options See L. =head1 SEE ALSO L =head1 AUTHOR Dominique Dumont =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2017 by Dominique Dumont. This is free software, licensed under: The GNU Lesser General Public License, Version 2.1, February 1999 =cut App-Cme-1.026/lib/App/Cme/Command/search.pm0000644000175000017500000000611013216003010016541 0ustar domidomi# # This file is part of App-Cme # # This software is Copyright (c) 2017 by Dominique Dumont. # # This is free software, licensed under: # # The GNU Lesser General Public License, Version 2.1, February 1999 # # ABSTRACT: Search the configuration of an application package App::Cme::Command::search ; $App::Cme::Command::search::VERSION = '1.026'; use strict; use warnings; use 5.10.1; use App::Cme -command ; use base qw/App::Cme::Common/; use Config::Model::ObjTreeScanner; sub validate_args { my ($self, $opt, $args) = @_; $self->check_unknown_args($args); $self->process_args($opt,$args); } sub opt_spec { my ( $class, $app ) = @_; return ( [ "search=s" => "string or pattern to search in configuration" , { required => 1 } ], [ "narrow-search=s" => "narrows down the search to element, value, key, summary, description or help", { regex => qr/^(?:element|value|key|summary|description|help|all)$/, default => 'all' } ], [ "quiet!" => "Suppress progress messages" ], $class->cme_global_options, ); } sub usage_desc { my ($self) = @_; my $desc = $self->SUPER::usage_desc; # "%c COMMAND %o" return "$desc [application] [ config_file ] -search xxx [ -narrow-search ... ] " ; } sub description { my ($self) = @_; return $self->get_documentation; } sub execute { my ($self, $opt, $args) = @_; my ($model, $inst, $root) = $self->init_cme($opt,$args); my @res = $root->tree_searcher( type => $opt->{narrow_search} )->search($opt->{search}); foreach my $path (@res) { print "$path"; my $obj = $root->grab($path); if ( $obj->get_type =~ /leaf|check_list/ ) { my $v = $obj->fetch; $v = defined $v ? $v : ''; print " -> '$v'"; } print "\n"; } } 1; __END__ =pod =encoding UTF-8 =head1 NAME App::Cme::Command::search - Search the configuration of an application =head1 VERSION version 1.026 =head1 SYNOPSIS =head1 DESCRIPTION Search configuration data with the following options: =over =item -search Specifies a string or pattern to search. C will a list of path pointing to the matching tree element and their value. See L for details on the path syntax. =item -narrow-search Narrows down the search to: =over =item element =item value =item key =item summary Summary text =item description description text =item help value help text =back =back Example: $ cme search multistrap my_mstrap.conf -search http -narrow value sections:base source -> 'http://ftp.fr.debian.org' sections:debian source -> 'http://ftp.uk.debian.org/debian' sections:toolchains source -> 'http://www.emdebian.org/debian' =head1 Common options See L. =head1 SEE ALSO L =head1 AUTHOR Dominique Dumont =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2017 by Dominique Dumont. This is free software, licensed under: The GNU Lesser General Public License, Version 2.1, February 1999 =cut App-Cme-1.026/README.pod0000644000175000017500000000457013216003010013037 0ustar domidomi=begin html =end html =head1 cme - Check or edit configuration data L provides a command to check or edit configuration data with L. L and L are quite modular: the configuration data that you can edit depend on the other C distributions installed on your system. For instance, to configure L or L, you need to install L. Then you can check your ssh configuration with this command: $ cme check ssh and you can modify it with: $ cme edit ssh The L contains a L L provides several commands. The most important are : =over =item check To check the content of the configuration file of an application. =item fix To fix the warnings of the configuration file. =item edit To launch cme interactive editor. This editor contains documentation and sanity checks to help user configure correctly their application. =back L user interface can be: =over =item * Graphical if L is installed. =item * a shell-like interface (plain or based on L). =item * based on curses if L is installed. =back By default, C will try to launch a GUI. =head2 Installation See L. Perl developers can also L =head2 What does "cme" means ? Nothing fancy: "Config Model Editor". The idea was to L. =head2 More information See =over =item * L man page =item * L (i.e. the wiki tab above) =item * L =item * The list of available models, interfaces and known configuration syntaxes: https://github.com/dod38fr/config-model/wiki/Available-models-and-backends =back App-Cme-1.026/META.json0000644000175000017500000000437113216003010013016 0ustar domidomi{ "abstract" : "Check or edit configuration data with Config::Model", "author" : [ "Dominique Dumont" ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 6.010, CPAN::Meta::Converter version 2.150010", "license" : [ "lgpl_2_1" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "App-Cme", "prereqs" : { "build" : { "requires" : { "Module::Build" : "0.34" } }, "configure" : { "requires" : { "Module::Build" : "0.34" } }, "runtime" : { "recommends" : { "Config::Model::CursesUI" : "0", "Tk" : "0" }, "requires" : { "App::Cmd::Setup" : "0", "Config::Model" : "2.116", "Config::Model::FuseUI" : "0", "Config::Model::Lister" : "0", "Config::Model::ObjTreeScanner" : "0", "Config::Model::SimpleUI" : "0", "Config::Model::TermUI" : "0", "Config::Model::Utils::GenClassPod" : "0", "Data::Dumper" : "0", "Encode" : "0", "File::HomeDir" : "0", "JSON" : "0", "Log::Log4perl" : "0", "Path::Tiny" : "0", "Pod::POM" : "0", "Pod::POM::View::Text" : "0", "Scalar::Util" : "0", "Tie::Hash" : "0", "YAML" : "0", "perl" : "5.010" } }, "test" : { "requires" : { "App::Cmd::Tester" : "0", "Probe::Perl" : "0", "Test::Command" : "0.08", "Test::File::Contents" : "0", "Test::More" : "0" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "mailto" : "ddumont at cpan.org", "web" : "https://github.com/dod38fr/cme-perl/issues" }, "homepage" : "https://github.com/dod38fr/config-model/wiki", "repository" : { "type" : "git", "url" : "https://github.com/dod38fr/cme-perl.git", "web" : "http://github.com/dod38fr/cme-perl" } }, "version" : "1.026", "x_serialization_backend" : "JSON::XS version 3.04" } App-Cme-1.026/build-from-git.md0000644000175000017500000000311513216003010014533 0ustar domidomi# How to build App::Cme from git repository `App::Cme` is build with [Dist::Zilla](http://dzil.org/). This pages details how to install the tools and dependencies required to build this module. ## Install tools and dependencies ### Debian, Ubuntu and derivatives Run $ sudo apt install libdist-zilla-perl libdist-zilla-app-command-authordebs-perl $ dzil authordebs --install $ sudo apt build-dep cme The [libdist-zilla-app-command-authordebs-perl package](https://tracker.debian.org/pkg/libdist-zilla-app-command-authordebs-perl) is quite recent (uploaded on Dec 2016 in Debian/unstable) and may not be available yet on your favorite distribution. ### Other systems Run $ cpamn Dist::Zilla $ dzil authordeps -missing | cpanm --notest $ dzil listdeps --missing | cpanm --notest NB: The author would welcome pull requests that explains how to install these tools and dependencies using native package of other distributions. ## Build App::Cme Run dzil build or dzil test `dzil` may complain about missing `EmailNotify` or `Twitter` plugin. You may ignore this or edit [dist.ini](dist.ini) to comment out the last 2 sections. These are useful only to the author when releasing a new version. `dzil` may also return an error like `Cannot determine local time zone`. In this case, you should specify explicitely your timezone in a `TZ` environement variable. E.g run `dzil` this way: TZ="Europe/Paris" dzil test The list of possible timezones is provided by [DateTime::TimeZone::Catalog](https://metacpan.org/pod/DateTime::TimeZone::Catalog) documentation. App-Cme-1.026/contrib/0000755000175000017500000000000013216003010013030 5ustar domidomiApp-Cme-1.026/contrib/bash_completion.cme0000644000175000017500000000772713216003010016701 0ustar domidomi# cme(1) completion -*- shell-script -*- # # # This file is part of App::Cmd::Cme # # This software is Copyright (c) 2011, 2014 by Dominique Dumont # # This is free software, licensed under: # # The GNU Lesser General Public License, Version 2.1, February 1999 # _cme_models() { MODELS=$(perl -MConfig::Model::Lister -e'print Config::Model::Lister::models;') COMPREPLY=( $( compgen -W "$MODELS" -- $cur ) ) } _cme_appli() { MODELS=$(perl -MConfig::Model::Lister -e'print Config::Model::Lister::applications;') COMPREPLY=( $( compgen -W "$MODELS" -- $cur ) ) } _cme_commands() { # use perl so that plugged in subcommand (like meta) are listed SUBCMDS=$(perl -MApp::Cme -e'print join("\n", grep {not /-/} App::Cme->new->command_names);') COMPREPLY=( $( compgen -W "$SUBCMDS" -- $cur ) ) } _cme_cmd_run() { vendor_scripts=$(perl -MConfig::Model::Lister -E 'my $p = $INC{"Config/Model/Lister.pm"}; $p =~ s/Lister.pm/scripts/; say $p') if [[ $COMP_CWORD -eq 2 ]] ; then scripts=$(find ~/.cme/scripts/ /etc/cme/scripts $vendor_scripts -type f ! -name '*~' -printf '%f\n' 2>/dev/null) COMPREPLY=( $( compgen -W "-list $scripts" -- $cur ) ) elif [[ $prev == '-arg' ]]; then script=$(find ~/.cme/scripts/ /etc/cme/scripts $vendor_scripts -type f -name ${COMP_WORDS[2]}) var=$( perl -E 'my @v; while(<>) { push @v, /\$(\w+)/g }; my %map = map { ($_ => 1)} @v; print map { "$_= " } sort keys %map;' $script); COMPREPLY=( $( compgen -W "$var" -- $cur ) ) elif [[ $prev != '-list' ]]; then COMPREPLY=( $( compgen -W "-arg -doc" -- $cur ) ) fi } _cme_handle_app_arg() { [[ $COMP_CWORD -eq 3 ]] && _cme_${COMP_WORDS[2]} 2> /dev/null ; } _cme_handle_cmd_arg() { [[ $COMP_CWORD -ge 2 ]] && _cme_cmd_${COMP_WORDS[1]} 2> /dev/null ; } _cme() { local cur COMPREPLY=() _get_comp_words_by_ref -n : cur prev global_options='-force-load -create -backend -trace -quiet -file' if [[ $COMP_CWORD -eq 1 ]] ; then _cme_commands elif _cme_handle_cmd_arg; then TRASH=1; # bash does not support empty then/elif elif [[ $COMP_CWORD -eq 2 ]] ; then _cme_appli elif ! _cme_handle_app_arg; then case $prev in -ui) COMPREPLY=( $( compgen -W 'tk curses shell' -- $cur ) ) ;; -dumptype) COMPREPLY=( $( compgen -W 'full preset custom' -- $cur ) ) ;; -model-dir|-root-dir|-fuse-dir) _filedir -d ;; -file) _filedir ;; *) case ${COMP_WORDS[1]} in check) COMPREPLY=( $( compgen -W "$global_options -strict" -- $cur ) ) ;; dump) COMPREPLY=( $( compgen -W "$global_options -dumptype" -- $cur ) ) ;; edit) COMPREPLY=( $( compgen -W "$global_options -ui -open-item" -- $cur ) ) ;; fix) COMPREPLY=( $( compgen -W "$global_options -from -filter" -- $cur ) ) ;; fusefs) COMPREPLY=( $( compgen -W "$global_options -fuse-dir -dfuse -dir-char" -- $cur ) ) ;; migrate) COMPREPLY=( $( compgen -W "$global_options" -- $cur ) ) ;; update) COMPREPLY=( $( compgen -W "$global_options" -- $cur ) ) ;; # modify completion could be much more elaborate... modify) COMPREPLY=( $( compgen -W "$global_options -save -backup" -- $cur ) ) ;; search) COMPREPLY=( $( compgen -W "$global_options -search -narrow-search" -- $cur ) ) ;; edit) COMPREPLY=( $( compgen -W "$global_options -open-item" -- $cur ) ) ;; esac esac fi } complete -F _cme cme App-Cme-1.026/MANIFEST.SKIP0000644000175000017500000000016513216003010013270 0ustar domidomi^debian/ ~$ \.ptkdb$ \.old$ dist.ini libconfig _build \.orig$ ^MYMETA.yml$ blib wr_root \.rej$ README.build-from-git App-Cme-1.026/META.yml0000644000175000017500000000240413216003010012641 0ustar domidomi--- abstract: 'Check or edit configuration data with Config::Model' author: - 'Dominique Dumont' build_requires: App::Cmd::Tester: '0' Module::Build: '0.34' Probe::Perl: '0' Test::Command: '0.08' Test::File::Contents: '0' Test::More: '0' configure_requires: Module::Build: '0.34' dynamic_config: 0 generated_by: 'Dist::Zilla version 6.010, CPAN::Meta::Converter version 2.150010' license: lgpl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: App-Cme recommends: Config::Model::CursesUI: '0' Tk: '0' requires: App::Cmd::Setup: '0' Config::Model: '2.116' Config::Model::FuseUI: '0' Config::Model::Lister: '0' Config::Model::ObjTreeScanner: '0' Config::Model::SimpleUI: '0' Config::Model::TermUI: '0' Config::Model::Utils::GenClassPod: '0' Data::Dumper: '0' Encode: '0' File::HomeDir: '0' JSON: '0' Log::Log4perl: '0' Path::Tiny: '0' Pod::POM: '0' Pod::POM::View::Text: '0' Scalar::Util: '0' Tie::Hash: '0' YAML: '0' perl: '5.010' resources: bugtracker: https://github.com/dod38fr/cme-perl/issues homepage: https://github.com/dod38fr/config-model/wiki repository: https://github.com/dod38fr/cme-perl.git version: '1.026' x_serialization_backend: 'YAML::Tiny version 1.70' App-Cme-1.026/CONTRIBUTING.md0000644000175000017500000000464313216003010013630 0ustar domidomi# How to contribute # ## Ask questions ## Yes, asking a question is a form of contribution that helps the author to improve documentation. Feel free to ask questions by sending a mail to [config-model-user mailing list](mailto:config-model-users@lists.sourceforge.net) ## Log a bug ## Please report issue on[cme issue tracker](https://github.com/dod38fr/cme-perl/issues). ## Source code structure ## The main parts of this modules are: * `bin/cme`: mostly cme command documentation * `contrib/bash_completion.cme`: the file that enable user to type 'cme[TAB]' and get the list of available sub-commands. See bash man page for more details on bash completion * `lib/App/Cme/Command/**.pm`: implementation of cme sub-commands * `t`: test files. Run the tests with `prove -l t` ## Edit source code from github ## If you have a github account, you can clone a repo and prepare a pull-request. You can: * run `git clone https://github.com/dod38fr/cme-perl` * edit files * run `prove -l t` to run non-regression tests There's no need to worry about `dzil`, `Dist::Zilla` or `dist.ini` files. These are useful to prepare a new release, but not to fix bugs. ## Edit source code from Debian source package ## You can also prepare a patch using Debian source package: For instance: * download and unpack `apt-get source cme` * jump in `cd cme-1.xxx` * useful to create a patch later: `git init` * commit all files: `git add -A ; git commit -m"committed all"` * edit files * run `prove -l t` to run non-regression tests * run `git diff` and send the output on [config-model-user mailing list](mailto:config-model-users@lists.sourceforge.net) ## Edit source code from Debian source package or CPAN tarball ## Non Debian users can also prepare a patch using CPAN tarball: * Download tar file from http://search.cpan.org * unpack tar file with something like `tar axvf App-Cme-1.xxx.tar.gz` * jump in `cd App-Cme-1.xxx` * useful to create a patch later: `git init` * commit all files: `git add -A ; git commit -m"committed all"` * edit files * run `prove -l t` to run non-regression tests * run `git diff` and send the output on [config-model-user mailing list](mailto:config-model-users@lists.sourceforge.net) ## Provide feedback ## Feedback is important. Please take a moment to rate, comment or add stars to this project: * [cme github](https://github.com/dod38fr/cme-perl) or [cme cpan ratings](http://cpanratings.perl.org/rate/?distribution=App-Cme) App-Cme-1.026/Build.PL0000644000175000017500000000404013216003010012662 0ustar domidomi# # This file is part of App-Cme # # This software is Copyright (c) 2017 by Dominique Dumont. # # This is free software, licensed under: # # The GNU Lesser General Public License, Version 2.1, February 1999 # # This file was automatically generated by Dist::Zilla::Plugin::ModuleBuild v6.010. use strict; use warnings; use Module::Build 0.34; my %module_build_args = ( "build_requires" => { "Module::Build" => "0.34" }, "configure_requires" => { "Module::Build" => "0.34" }, "dist_abstract" => "Check or edit configuration data with Config::Model", "dist_author" => [ "Dominique Dumont" ], "dist_name" => "App-Cme", "dist_version" => "1.026", "license" => "lgpl", "module_name" => "App::Cme", "recommends" => { "Config::Model::CursesUI" => 0, "Tk" => 0 }, "recursive_test_files" => 1, "requires" => { "App::Cmd::Setup" => 0, "Config::Model" => "2.116", "Config::Model::FuseUI" => 0, "Config::Model::Lister" => 0, "Config::Model::ObjTreeScanner" => 0, "Config::Model::SimpleUI" => 0, "Config::Model::TermUI" => 0, "Config::Model::Utils::GenClassPod" => 0, "Data::Dumper" => 0, "Encode" => 0, "File::HomeDir" => 0, "JSON" => 0, "Log::Log4perl" => 0, "Path::Tiny" => 0, "Pod::POM" => 0, "Pod::POM::View::Text" => 0, "Scalar::Util" => 0, "Tie::Hash" => 0, "YAML" => 0, "perl" => "5.010" }, "script_files" => [ "bin/cme" ], "test_requires" => { "App::Cmd::Tester" => 0, "Probe::Perl" => 0, "Test::Command" => "0.08", "Test::File::Contents" => 0, "Test::More" => 0 } ); my %fallback_build_requires = ( "App::Cmd::Tester" => 0, "Module::Build" => "0.34", "Probe::Perl" => 0, "Test::Command" => "0.08", "Test::File::Contents" => 0, "Test::More" => 0 ); unless ( eval { Module::Build->VERSION(0.4004) } ) { delete $module_build_args{test_requires}; $module_build_args{build_requires} = \%fallback_build_requires; } my $build = Module::Build->new(%module_build_args); $build->create_build_script;