How do I automate the initial CPAN configuration? - perl

Here How do I automate CPAN configuration? I've found some answers which led to some questions.
I tried cpan -j config.pm, but as far as I can see it is meant for per installation usage, not for changing the config-file permanently.
With the $CPAN::Config-method the force CPAN::FirstTime to not default to manual-part didn't work here so I tried without it:
#!/usr/bin/perl
use strict;
use warnings;
use Config;
use CPAN;
use CPAN::FirstTime;
$ENV{PERL_MM_USE_DEFAULT}=1;
$ENV{PERL_MM_NONINTERACTIVE}=1;
$ENV{AUTOMATED_TESTING}=1;
my $cpan_home = '/home/me/.cpan';
mkdir $cpan_home or die $! if not -d $cpan_home;
mkdir "$cpan_home/CPAN" or die $! if not -d "$cpan_home/CPAN";
CPAN::FirstTime::init( "$cpan_home/CPAN/MyConfig.pm" );
delete $CPAN::Config->{links};
$CPAN::Config->{applypatch} = '';
# ...
$CPAN::Config->{build_dir} = "$cpan_home/build";
$CPAN::Config->{cpan_home} = $cpan_home;
$CPAN::Config->{histfile} = "$cpan_home/histfile";
$CP$CPAN::Config->{keep_source_where} = "$cpan_home/sources";
$CPAN::Config->{make_install_make_command} = 'sudo make';
$CPAN::Config->{mbuild_install_build_command} = 'sudo ./Build';
$CPAN::Config->{prefs_dir} = "$cpan_home/prefs";
# ...
$CPAN::Config->{yaml_module} = 'YAML';
CPAN::HandleConfig->commit("$cpan_home/CPAN/MyConfig.pm");
CPAN::install('Bundle::CPAN');
# ...
# etc.
exit 0;
Is this OK? The only bad thing that I have noticed so far is the waiting, until the cpan-mirror-urls are found.
And what is the delete $CPAN::Config->{links}; for?

It looks like you are doing a lot of work. What are you trying to accomplish?
If you want to change the configuration file permanently, just change the configuration file. It's Perl code, so I think you can do everything you need, such as setting the root directory, right in the config file without having to deal with CPAN.pm.

Related

If I declare a package with multiple levels of embedding, does the module named as the leaf node need to be in a subdirctory?

I am dealing with some legacy Perl code, which I want to change as little as possible. The main script, call it "myscript.pl" has a structure like this
use MyCompany::ABDoc::HTMLFile;
use MyCompany::ABDoc::JavaScriptFile 2014.004_015;
print "Howdy"
...
The HTMLFile.pm module looks like this
package MyCompany::AMDoc::HTMLFile;
...
I am troubleshooting my_script.pl, and wish to run it from the command line. (It normally is triggered by a Jenkins job). When I try
perl -d ./my_script.pl
I get a message about HTMLFile.pm not being found. This is because hte HTMLFile.pm actually exists at the same level as my_script.pl in the filesystem.
If this was my own script, and I had the freedom to move things around, I would create directory structure
MyCompany/AMDoc/HtmlFile.pm
and I know the script would work. But I am reluctant to do this, because somehow, this all runs fine when triggered by the Jenkins job.
So is it possible to run this code, from the command line, without moving anything? I just want to do some troubleshooting. I haven't found discussion in the Perl documentation about what kinds of command line flags, such as "-I", might help me out here.
I would create directory structure MyCompany/AMDoc/HtmlFile.pm and I know the script would work.
No, moving the file to MyCompany/AMDoc/HTMLFile.pm relative to the script would not work unless the script already takes steps to add its directory to #INC.[1]
For example, adding the following in the script would achieve that:
use FindBin qw( $RealBin );
use lib $RealBin;
This can also be done from without
perl -I "$dir" "$dir"/my_script.pl # General case
perl -I . ./my_script.pl # Specific case
So is it possible to run this code, from the command line, without moving anything?
No, not without modifying the script.[2]
According to what you gave us, it has to be accessible as MyCompany/AMDoc/HTMLFile.pm relative to a directory in #INC.
It would happen to work if the script's current work directory happened to match the directory in which the script is found in old version of Perl. But that's just a fluke. These frequently don't match.
Well, you could use something of the form
perl -e'
use FindBin qw( $RealBin );
my $s = shift;
push #INC, sub {
my ( undef, $path ) = #_;
my ( $qfn ) = $path =~ m{^MyCompany/AMDoc/(.*)}s
or return;
open( my $fh, '<', $qfn )
or return;
return $fh;
};
do( $s ) or die( $# // $! );
' ./my_script.pl
Even then, that expects the script to end in a true value.
My initial assumption was wrong. My code was actually running on a Kubernetes pod, and that pod was configured with the correct directory structure for the module. In addition, PERL5LIB is set in the pod.
PERL5LIB=/perl5lib/perl-modules:/perl5lib/perl-modules/cpan/lib64/perl5:/perl5lib/perl-modules/cpan/share/perl5
and sure enough, that very first path has the path to my module.

Receiving strange error when piping yes into rm -r command

I receive the following
yes: standard output: broken pipe
yes: write error
when executing the following line in perl
system("cd /tmp; yes | rm -r directory1 directory2 file3 > /dev/null 2>&1");
The files and directories still get deleted but the message is annoying and would prefer not to see it.
any help Thanks
Not really a Perl question.
The error comes from yes, so you would have to redirect the error stream of yes, too.
... ; yes 2>/dev/null | rm -r ...
But why don't you forget about yes and try rm -rf ... ?
You say Perl, but your question says Unix shell.
If you're going to use Perl, do it in Perl:
use File::Path qw(remove_tree);
use Cwd;
my $current_directory = getcwd();
chdir "tmp";
remove_tree("directory1", "directory2, "file3");
chdir $current_directory;
This will do the same thing as your system command. But, this will work not only on Unix systems, but also on strange and peculiar operating systems that no man should be subject to (Cough, Windows! Cough).
It also will work even if someone has created an alias rm command, or has implemented their own rm command. Whenever you shell out via the system command, you leave that nice safe Perl environment and head directly into the dangerous neighborhood of the Operating System. Even worse, you're placing your life into the hands of a shell that might not be the shell you use either.
The module File::Path is a standard module that comes with Perl. Same goes with Cwd I used to save your original directory, so we could come back to it at the end of your code.
By the way, you probably want some error checking there too:
use File::Path qw(remove_tree);
use Cwd;
my $current_directory = getcwd();
if chdir "tmp" {
remove_tree("directory1", "directory2, "file3");
chdir $current_directory;
}
else {
print "Whoops! I can't change to directory 'tmp'"
}
This way, you are only doing the remove_tree if you've actually changed into directory tmp.
If this is your temporary working directory. That is, you've created it, so you have a place to put files, , you might should look at the File::Temp module.
File::Temp (which is also a standard Perl module) can create the temporary directory for you, and makes sure that it's unique, so you're not trampling over someone else running your program at the same time. Even better, it will also clean up after itself and delete that temporary directory once complete.
use File::Temp;
my $temp_dir = File::Temp->newdir;
Now, you can put all of your temporary files into $temp_dir. When the program ends, the temporary directory and all files you placed there are automatically deleted.
Although not part of the standard Perl functions, these modules are part of Perl's distribution and are always available. There should be no reason at all not to use them. If you're using the system command, there is probably a standard Perl module that you should be using instead.
Browse through the list of Perl modules in the Perdoc Webpage. Just make sure you select the version of Perl you're using.
To prevent questions, use -f or --interactive=never if implemented. See man rm.

Problems using the HTML::Template module

I'm unable to execute the HTML::Template function in the CGI.
I'm following a simple tutorial that I found here: http://metacpan.org/pod/HTML::Template
I created a new file on my server in the home path as test.tmpl.
I created a new file named frt.cgi ... (is that the issue here? should it be a different file extention??)
#!/usr/local/bin/perl -w
use HTML::Template;
# open the html template
my $template = HTML::Template->new(filename => '/test.html');
# fill in some parameters
$template->param(HOME => $ENV{HOME});
$template->param(PATH => $ENV{PATH});
# send the obligatory Content-Type and print the template output
print "Content-Type: text/html\n\n", $template->output;
I've modified the 1st line to reflect my host provided program path for perl. I don't know what the -w does I just know I've tried this with and without it. Also I've tried changing the code a bit like this:
use warnings;
use strict;
use CGI qw(:standard);
use HTML::Template;
I've searched...
https://stackoverflow.com/search?q=HTML%3A%3ATEMPLATE+&submit=search
https://stackoverflow.com/search?q=HTML%3A%3ATEMPLATE
https://stackoverflow.com/search?q=HTML%3A%3ATEMPLATE+PERL&submit=search
Yet I still do not see the answer.
I even searched google for .TMPL Encoding because I thought there may be some special type needed. Please help.
If you look in your server logs, you'll probably see an error message along the lines of:
HTML::Template->new() : Cannot open included file /test.html : file not found.
You need to provide the path on the file system, not a URI relative to the generated document.
First, you likely specified the wrong path - change /test.html to test.html.
Also, it is possible that there is no $ENV{HOME} variable in your system so set up flag die_on_bad_params to 0:
my $template = HTML::Template->new(
filename => 'test.html',
die_on_bad_params => 0,
);
Also, don't forget to mark your Perl file as executable by chmod 755.
Option -w makes Perl to enable warnings, so there is no point to write use warnings; afterwards.
You can check what Perl command line options do by using module B::Deparse, like this ($^W variable disables/enables warnings):
perl -w -MO=Deparse -e "print;"
This would print:
BEGIN { $^W = 1; }
print $_;

Setting CHOWN to 0 in perl archive tar

I'm trying to set CHOWN to 0 so that when extracted as root the files aren't chown'd to the uid saved in the archive. This doesn't seem to work.
use Archive::Tar;
use Getopt::Long qw( :config pass_through );
my $tarballName = $ARGV[0];
my $testfix = Archive::Tar->new();
$testfix::CHOWN=0;
$testfix->read ($tarballName);
print "CHOWN=$testfix::CHOWN \n";
$testfix->extract()
The code above prints CHOWN=0, yet when I add print "CHOWN=$CHOWN \n"; to archive::tar.pm and run it I get :
CHOWN=0
CHOWN in tar.pm=1
Is this the correct way to change this setting?
You should set $Archive::Tar::CHOWN, not $testfix::CHOWN. Moreover, you declare $testfix as an object, but later use it as a prefix - these two concepts are quite different!
No. $testfix::CHOWN is the $CHOWN variable in the testfix package, which isn't consulted by anything except your print statement.
$Archive::Tar::CHOWN = 0;

How do I automate CPAN configuration?

The first time you run cpan from the command line, you are prompted for answers to various questions. How do you automate cpan and install modules non-interactively from the beginning?
Since it hasn't been mentioned yet, cpanminus is a zero-conf cpan installer. And you can download a self-contained executable if it isn't available for your version control.
The cpanm executable is easily installed (as documented in the executable itself) with:
curl -L http://cpanmin.us | perl - --self-upgrade
# or
wget -O - http://cpanmin.us | perl - --self-upgrade
I was looking for an easy solution for this as well and found that this works:
(echo y;echo o conf prerequisites_policy follow;echo o conf commit)|cpan
Just thought I would post it here in case anyone else comes along.
Make your own CPAN.pm config file. The recent versions of the cpan command have a -J switch to dump the current config and a -j switch to load whatever config you like.
I added these switches because we were distributing CPAN on a CD, back in the days when a MiniCPAN could fit in under 700Mb. You'd run cpan as normal but with an added option:
% cpan -j /CD/Volume/path/cpan_config.pm ....
In that example, the config would set the URL list to the CD path. I've long since lost the source files, but I recall there was a way that it figured out dynamically where it was, or maybe had a program that did and saved the config somewhere.
Recent versions of CPAN.pm ask as first question whether the rest of the configuration should be run automatically, so it is advisable to upgrade CPAN.pm (manually) first: tarballs, repo.
One way is to take the CPAN/Config.pm (or ~/.cpan/CPAN/MyConfig.pm) created after one run from one system, and install it as ~/.cpan/CPAN/MyConfig.pm on the system you want to automate. Another way is to run the following to create the MyConfig.pm file for you (one thing missing below is the actual values for the urllist parameter which you will have to fill in with appropriate values for CPAN mirrors):
#!/usr/bin/perl
use strict;
use Config;
$ENV{PERL_MM_USE_DEFAULT}=1;
$ENV{PERL_MM_NONINTERACTIVE}=1;
$ENV{AUTOMATED_TESTING}=1;
# get the path to the library
my $libpath = $Config{privlib};
# force CPAN::FirstTime to not default to manual
# setup, since initial CPAN setup needs to be automated
{
local #ARGV = "$libpath/CPAN/FirstTime.pm";
my #source = <>;
$source[72] =~ s/\byes\b/no/ or die "Could not auto configure CPAN";
eval join('', #source) or die "Error executing CPAN::FirstTime: $#";
}
CPAN::FirstTime::init("$libpath/CPAN/Config.pm");
delete $CPAN::Config->{links};
$CPAN::Config->{auto_commit} = '0';
$CPAN::Config->{check_sigs} = '0';
$CPAN::Config->{halt_on_failure} = '0';
$CPAN::Config->{make_install_make_command} = '/usr/bin/make';
$CPAN::Config->{mbuild_arg} = '';
$CPAN::Config->{mbuildpl_arg} = '';
$CPAN::Config->{mbuild_install_arg} = '';
$CPAN::Config->{show_upload_date} = '';
$CPAN::Config->{tar_verbosity} = '1';
$CPAN::Config->{trust_test_report_history} = '0';
$CPAN::Config->{use_sqlite} = '0';
$CPAN::Config->{yaml_load_code} = '0';
$CPAN::Config->{urllist}
= [qw(http://... ftp://... etc...)];
$CPAN::Config->{connect_to_internet_ok} = '1';
$CPAN::Config->{perl5lib_verbosity} = 'v';
$CPAN::Config->{prefer_installer} = 'MB';
$CPAN::Config->{build_requires_install_policy} = 'no';
$CPAN::Config->{term_ornaments} = '1';
$CPAN::Config->{mbuild_install_build_command} = './Build';
mkdir ".cpan/CPAN" or die "Can't create .cpan/CPAN: $!";
CPAN::Config->commit(".cpan/CPAN/MyConfig.pm");
CPAN::install('Bundle::CPAN');
CPAN::install('JSON');
CPAN::install('JSON::XS');
# etc.
exit 0;