install-3.tcl

Install packages -- actual installation

Location:
/packages/acs-admin/www/install/install-3.tcl

Related Files

[ hide source ] | [ make this the default ]

File Contents

ad_page_contract {
    Install packages -- actual installation

    @param install Tcl list of packages to install in the order in which they should be installed
} {
    {repository_url ""}
}


#####
#
# Display progress bar
#
#####

ad_progress_bar_begin \
    -title "Installing Packages" \
    -message_1 "Installing selected packages, please wait ..." \
    -message_2 "We will continue automatically when installation is complete."

#####
#
# Get packages to install
#
#####

apm_get_package_repository -repository_url $repository_url -array repository

set install [ad_get_client_property acs-admin install]
if { [llength $install] == 0 } {
    ns_log Notice "install-3.tcl: Nothing to install. Is this a double-click?"
}

# We unset the client property so we won't install these packages twice
ad_set_client_property acs-admin install {}


#
# Perform a topological sort for the right install order
#
set install_order ""
set to_install $install
ns_log notice "to_install: $to_install"

while {[llength $to_install] > 0} {

    foreach package_key $to_install {
        array unset version
        array set version $repository($package_key)

        set satisfied_p 1
        foreach req [concat $version(embeds) $version(extends) $version(requires)] {
            lassign $req pkg req_version

            #
            # A package can be installed, when its requirements are
            # installed before the package. All other dependencies
            # were checked earlier.
            #

            if { $pkg in $to_install } {
                set satisfied_p 0
                #ns_log notice "we have to delay $pkg"
                break
            }
        }
        if {$satisfied_p} {
            lappend install_order $package_key
            set pos [lsearch $to_install $package_key]
            set to_install [lreplace $to_install $pos $pos]
        }
    }
    #ns_log notice "iteration: \nto_install: $to_install\ninstall_order: $install_order"
}

ns_log notice "Install packages in this order: $install_order"

#####
#
# Install packages
#
#####

set success_p 1

foreach package_key $install_order {
    ns_log Notice "Installing $package_key"

    array unset version
    array set version $repository($package_key)

    if { [info exists version(download_url)] && $version(download_url) ne "" } {
        ns_write [subst {
            <p>Transferring $version(download_url) ...
            <script nonce='[security::csp::nonce]'>window.scrollTo(0,document.body.scrollHeight);</script>
        }]
        set spec_file [apm_load_apm_file -url $version(download_url)]
        if { $spec_file eq "" } {
            set msg "Error downloading package $package_key from $version(download_url). Installing package failed."
            ns_write [subst {
                <p>$msg
                <script nonce='[security::csp::nonce]'>window.scrollTo(0,document.body.scrollHeight);</script>
            }]
            ns_log Error $msg
            set success_p 0
            continue
        }
        ns_write [subst {
            Done<br>
            <script nonce='[security::csp::nonce]'>window.scrollTo(0,document.body.scrollHeight);</script>
        }]
        set package_path "[apm_workspace_install_dir]/$package_key"
    } else {
        set spec_file $version(path)
        set package_path "$::acs::rootdir/packages/$package_key"
    }

    set final_version_name $version(name)

    if { [apm_package_version_installed_p $version(package.key) $version(name)] } {
        # Already installed.

        # Enable this version, in case it's not already enabled
        if { ![apm_package_enabled_p $version(package.key)] } {
            ns_log Notice "Package $version(package.key) $version(name) is already installed but not enabled, enabling"
            apm_version_enable -callback apm_dummy_callback [apm_highest_version $version(package.key)]
        } else {
            ns_log Notice "Package $version(package.key) $version(name) is already installed and enabled, skipping"
        }
        continue
    }

    # Determine if we are upgrading or installing.
    if { [apm_package_upgrade_p $package_key $final_version_name] == 1} {
        ns_log Debug "Upgrading package [string totitle $version(package-name)] to $final_version_name."
        set upgrade_p 1

        set initial_version_name [apm_highest_version_name $package_key]
    } else {
        set upgrade_p 0
        set initial_version_name ""
    }

    # Find out which script is appropriate to be run.
    set data_model_files [apm_data_model_scripts_find \
                                   -upgrade_from_version_name $initial_version_name \
                                   -upgrade_to_version_name $final_version_name \
                                   -package_path $package_path \
                                   $package_key]

    ns_log Debug "Data model scripts: \nupgrade_from_version_name = $initial_version_name\nupgrade_to_version_name=$final_version_name\npackage_path=$package_path\npackage_key=$package_key\n => $data_model_files"

    ns_write [subst {
        <p>Installing $package_key ...<br>
        <script nonce='[security::csp::nonce]'>window.scrollTo(0,document.body.scrollHeight);</script>
    }]

    # Install the package -- this actually copies the files into the
    # right place in the filesystem and backs up any old files

    set version_id [apm_package_install \
                        -enable \
                        -install_from_repository \
                        -package_path $package_path \
                        -load_data_model \
                        -data_model_files $data_model_files \
                        $spec_file]

    if { $version_id == 0 } {
        # Installation of the package failed and we shouldn't continue with installation
        # as there might be packages depending on the failed package. Ideally we should
        # probably check for such dependencies and continue if there are none.
        set success_p 0
    } else {
        ns_write "... installation OK <br>\n"
    }

    if {$success_p} {
        #
        # The update has finished successfully. Since all the new
        # files were sourced, the actual connection thread is already
        # up to date.  In order to provide this code to the other
        # threads, it is necessary to update the internal
        # blueprint. This works different in NaviServer and AOLserver,
        # and is supported only by NaviServer for the time being.
        #
        # Other options:
        #
        #   - run apm_package_install via "ns_eval": does not work,
        #     since "ns_eval" runs a script twice, a package can only
        #     be installed once.
        #
        #   - run parts of apm_package_install: e.g. loading just the
        #     procs does not work, when it depends e.g. on package
        #     parameters, which have as well be updated in the
        #     blueprint.
        #
        #   - fix the behavior in AOLserver
        #
        if {[namespace which ::nstrace::statescript] ne ""} {
            #
            # NaviServer variant:
            #   - nstrace::statescript produces the blueprint
            #   - "ns_ictl save" updates it in the server
            #
            ns_ictl save [nstrace::statescript]
            ns_write "... blueprint updated <br>\n"
        } else {
            #
            # AOLserver: _ns_savenamespaces produces the update script
            # and updates the blueprint, .... but it kills the
            # internal state of the server. After running this
            # command, e.g. all ns_sets are gone, later commands run
            # into problems.
            #
            # _ns_savenamespaces
        }
    } else {
        #
        # At least one update has failed. Since it is not clear whether or
        # not library files were sourced, it is necessary to delete this
        # thread asap to avoid potential confusion with already updated
        # procs.
        #
        ns_ictl markfordelete
    }
    ns_write [subst {
        <script nonce='[security::csp::nonce]'>window.scrollTo(0,document.body.scrollHeight);</script>
    }]
}

if {$success_p && [namespace which ::nstrace::statescript] ne ""} {

    foreach package_key $install_order {
        apm_bootstrap_load_libraries -init $package_key
        ns_write "$package_key: loading init files done"
        ns_log notice "$package_key: loading init files done"
    }
    ns_ictl save [nstrace::statescript]
}

#####
#
# Done
#
#####

ad_progress_bar_end -url [export_vars -base install-4 { repository_url success_p }]


# Local variables:
#    mode: tcl
#    tcl-indent-level: 4
#    indent-tabs-mode: nil
# End: