Opened 17 years ago

Closed 17 years ago

Last modified 12 years ago

#8 closed enhancement (wontfix)

shepherd should try really hard to fulfill any CPAN module dependencies with a local CPAN archive

Reported by: lincoln Owned by: lincoln
Priority: blocker Milestone: 1.0
Component: Shepherd Version: 0.3
Keywords: KLZPQFRwwcf Cc:

Description

shepherd should try really hard to fulfill any CPAN module dependencies with a local CPAN archive in ~/.shepherd/deps/ or similar. (i.e. without requiring root or write priviledges in /usr/lib/perl*/..).

a key goal of shepherd is that it auto-updates itself and any components without requiring manual intervention. that goal should also apply to any dependencies that CAN be updated provided it isn't too hard to do. (i.e. CPAN modules: yes, Javascript system libarries: not necessarily).

Change History (6)

comment:1 Changed 17 years ago by lincoln

Milestone: 2.0

Deferring this for 2.0 or future - marking as too hard for now.

Here is some code that works some of the time, but many CPAN modules just have external dependencies that cannot be met.

On trivial ones (like Cwd, LWP etc), it works just fine.

On more complex ones with external depencendies (e.g. GD, Javascript) it will consistently fail.

The real problem is that many CPAN install scripts expect to always be interactive and ask questions. Installation of these will almost always fail.

Unless anyone has any smart idea on how to progress further with this, i say it be scrapped for the time being.

The code ‘as is’ is as per below.

Shepherd itself ‘tests’ all components when they’re installed and notices any “Can't locate (.*) in @INC” messages as a hint of a missing module.

It then tries to install it.

The idea is that components called from shepherd can pick up ‘locally installed’ modules via this code (at the beginning of each module):

  [..]
  BEGIN { unshift(@INC, $ENV{SHEPHERD_INC}) if (defined $ENV{SHEPHERD_INC}); }
  use myCustomModule;
  [..]

Shepherd passes any additions to @INC (where perl looks for modules) via the SHEPHERD_INC environment variable.

$ svn diff
Index: applications/shepherd
===================================================================
--- applications/shepherd       (revision 283)
+++ applications/shepherd       (working copy)
@@ -67,6 +67,8 @@
 
 my $ARCHIVE_DIR = "$CWD/archive";
 my $LOG_DIR = "$CWD/log";
+my $DEPS_DIR = "$CWD/deps";
+$ENV{SHEPHERD_INC} = $DEPS_DIR."/i386-linux-thread-multi";
 
 my @options;
 my $opt = {};
@@ -342,7 +344,9 @@
 sub test_proggy
 {
     my ($proggy, $progtype) = @_;
+    my %already_tried_local_dep;
 
+RETRY_TEST:
     &log("Testing $proggy...\n");
 
     my $ldir = query_ldir($proggy, $progtype);
@@ -371,7 +375,21 @@
            $modname =~ s#\.pm##g;      # remove .pm suffix
            $components->{$proggy}->{laststatus} .= ": missing '".$modname."' modle.";
-           &log("Probably failued due to dependency on missing module '".modname."'\n");
+           &log("Probably failued due to dependency on missing module '$modname'\n");
+
+           # try to install a local copy of this module
+           # NOTE: we may iterate around this multiple times to meet multiple dependencies.
+           # because of that, track dependencies on a per-modname basis
+           if (!defined $already_tried_local_dep{$modname}) {
+               &log("Seeing if we can auto install '$modname' dependency...\n");
+               $already_tried_local_dep{$modname} = 1;
+               if (try_install_dependency($modname) == 0) {
+                   &log("Installed a local copy of '$modname', retesting '$proggy' to see if it now functions...\n");
+                   goto RETRY_TEST;
+               }
+           }
+
+           &log("Couldn't figure out how to auto-install all dependencies. Manual intervention required!\n");
        }
 
        &log("\n");
@@ -437,7 +455,7 @@
        my $progtype = $components->{$proggy}->{type};
        my $try_count = 0;
 
-RETRY:
+RETRY_CHECK:
        $try_count++;
        $result = test_proggy($proggy, $components->{$proggy}->{type});
        $components->{$proggy}->{ready} = $result;
@@ -449,7 +467,7 @@
            system(query_filename($proggy, $progtype) . " ". query_config($proggy, 'option_config') . " 2>&1");
            chdir ($CWD);

 
-           goto RETRY;
+           goto RETRY_CHECK;
        }
     }
 }
@@ -539,6 +558,66 @@
     }
 }

+# try to install a (local copy) of a CPAN module to meet a missing dependency
+sub try_install_dependency
+{
+    my $modname = shift;
+
+    if ($< == 0) {
+       &log("Running as 'root': not auto-installing CPAN modules.\n");
+       return -1;
+    }
+
+    if (!defined $ENV{HOME}) {
+       &log("Could not determine HOME directory: not auto-installing CPAN modules.\n");
+       return -1;
+    }
+
+    # create our modules directory if it doesn't exist
+    -d $DEPS_DIR or mkdir $DEPS_DIR or &log("Cannot create directory $DEPS_DIR: $!\n");
+    chdir($DEPS_DIR);
+
+    my $CPAN_DIR = $ENV{HOME}."/.cpan"; # ~/.cpan/
+    -d $CPAN_DIR or mkdir $CPAN_DIR;
+    if (!-d $CPAN_DIR) {
+       &log("Directory '$CPAN_DIR' could not be created: $!: not auto-installing CPAN modules.\n");
+       return -1;
+    }
+
+    $CPAN_DIR .= "/CPAN"; # ~/.cpan/CPAN/
+    -d $CPAN_DIR or mkdir $CPAN_DIR;
+    if (!-d $CPAN_DIR) {
+       &log("Directory '$CPAN_DIR' could not be created: $!: not auto-installing CPAN modules.\n");
+       return -1;
+    }
+
+    # create a user-local CPAN installation
+    if (!(open(F,">$CPAN_DIR/MyConfig.pm"))) {
+       &log("Could not open $CPAN_DIR/MyConfig.pm for writing: $!\n");
+       return -1;
+    }
+
+    # override only specific settings
+    printf F "# CPAN user override settings for Shepherd\n\n";
+    printf F "# pull in system settings\n";
+    printf F "use CPAN::Config;\n\n";
+    printf F "# override specific settings\n";
+    printf F '$'."CPAN::Config->{'build_dir'} = q[%s/build];\n",$DEPS_DIR;
+    printf F '$'."CPAN::Config->{'cpan_home'} = q[%s];\n",$DEPS_DIR;
+    printf F '$'."CPAN::Config->{'histfile'} = q[%s/histfile];\n",$DEPS_DIR;
+    printf F '$'."CPAN::Config->{'inhibit_startup_message'} = q[1];\n";
+    printf F '$'."CPAN::Config->{'keep_source_where'} = q[%s/sources];\n",$DEPS_DIR;
+    printf F '$'."CPAN::Config->{'prerequisites_policy'} = q[follow];\n";
+    printf F '$'."CPAN::Config->{'makepl_arg'} = q[PREFIX=$DEPS_DIR SITELIBEXP=$DEPS_DIR LIB=$DEPS_DIR INSTALLMAN1DIR=$DEPS_DIR/man INSTALLMAN3DIR=$DEPS_DIR/man INSTALLSITEMAN1DIR=$DEPS_DIR/man INSTALLSITEMAN3DIR=$DEPS_DIR/man];\n";
+    printf F "1;\n__END__\n";
+    close(F);
+
+    # call CPAN and ask it to install
+    system("cpan -fi $modname");
+
+    return 0;
+}
+
 sub query_grabbers
 {
     my ($conf, $val) = @_;

comment:2 Changed 17 years ago by lincoln

Resolution: wontfix
Status: newclosed

comment:3 Changed 14 years ago by anonymous

Milestone: 2.0

comment:4 Changed 12 years ago by Wednesday

Keywords: KLZPQFRwwcf added
Milestone: 1.0

That's way the bestest asnwer so far!

comment:5 Changed 12 years ago by Coltin

That's 2 cleevr by half and 2x2 clever 4 me. Thanks!

comment:6 Changed 12 years ago by lvbzkkuy

8U95lY <a href="http://bukglwnwwalj.com/">bukglwnwwalj</a>

Note: See TracTickets for help on using tickets.