Forum OpenACS Development: Re: Run APM Upgrades automatically

Collapse
Posted by Héctor Romojaro on
Hi Malte,

i have been also dealing with some CI/CD as well, including automated packages upgrade, automated testing, and so on...

I wrote the following code, which is called from a pipeline job on our CI tool, via curl, in order to automatically upgrade all possible packages, or a single package + its dependencies, if package_key is passed.

In our setup, this is run in a brand new instance inside a docker container, and the error.log is parsed by our CI tool, grepping for certain strings to determine the result of the job, if upgrades were successful or not. The error.log is then made available as a job artifact, in case it needs to be inspected to check if something went wrong.

Hope it helps, i have simplified it a bit removing certain particularities of our own setup, so test it at your own risk :)

ad_page_contract {
    Upgrade required packages
} {
    {package_key:token ""}
}

# Headers
ns_headers 200 text/plain

#
# Vars
#
set pad 80
set all_fine true
#
# Print header
#
ns_write "* OpenACS package upgrade\n"
ns_write "=========================\n"
#
# Retrieve all spec files
#
set packages_spec_files     [apm_scan_packages "$::acs::rootdir/packages"]
set workspace_spec_files    [apm_scan_packages [apm_workspace_install_dir]]
set workspace_filenames     [list]
foreach spec_path $workspace_spec_files {
    lappend workspace_filenames [file tail $spec_path]
}
set all_spec_files $workspace_spec_files
foreach spec_path $packages_spec_files {
    set spec_filename [file tail $spec_path]
    if {$spec_filename ni $workspace_filenames} {
        lappend all_spec_files $spec_path
    }
}
#
# Parse the files and make a list of available packages to upgrade
#
set packages_to_upgrade [list]
foreach spec_file $all_spec_files {
    array set version    [apm_read_package_info_file $spec_file]
    set this_version     $version(name)
    set this_package_key $version(package.key)
    #
    # Filter by package_key, if passed as an argument, and check for upgrades
    #
    if {($package_key eq "" || $package_key eq $this_package_key) &&
        [apm_package_supports_rdbms_p -package_key $this_package_key] &&
        [apm_package_registered_p $this_package_key] &&
        [apm_package_installed_p $this_package_key] &&
        [apm_higher_version_installed_p $this_package_key $this_version] eq 1
    } {
        #
        # Add the package to the list
        #
        lappend packages_to_upgrade $this_package_key
    }
}
#
# Are there packages to upgrade?
#
if {$packages_to_upgrade ne ""} {
    #
    # Dependency check
    #
    apm_get_package_repository -array repository
    apm_get_installed_versions -array installed
    ns_log notice "ci-packages-upgrade: run apm_dependency_check_new with <$packages_to_upgrade>"
    array set result [apm_dependency_check_new \
                          -repository_array repository \
                          -package_keys $packages_to_upgrade]
    ns_log notice "ci-packages-upgrade: apm_dependency_check_new with <$packages_to_upgrade>: [array get result]"
    if {$result(status) eq "ok"} {
        #
        # Do the upgrade
        #
        foreach package_key $result(install) {
            #
            # As we may have new packages included by the dependency check,
            # determine if we are upgrading or installing.
            #
            set spec_file       [apm_package_info_file_path $package_key]
            array set version   [apm_read_package_info_file $spec_file]
            set new_version     $version(name)
            if { [apm_package_upgrade_p $package_key $new_version] == 1} {
                set installed_version [apm_highest_version_name $package_key]
                ns_write "[ad_pad -right "* Upgrade $package_key ($installed_version -> $new_version) ..." $pad " "]"
                ns_log Warning "ci-packages-upgrade: $package_key ($installed_version -> $new_version)"
            } else {
                set installed_version ""
                ns_write "[ad_pad -right "* Install $package_key ($new_version) ..." $pad " "]"
                ns_log Warning "ci-packages-upgrade: $package_key (fresh install $new_version)"
            }
            #
            # Select SQL scripts
            #
            set data_model_files [apm_data_model_scripts_find \
                                      -upgrade_from_version_name $installed_version \
                                      -upgrade_to_version_name $new_version \
                                      $package_key]
            ns_log Warning "ci-packages-upgrade: $package_key datamodel files: $data_model_files"
            #
            # Upgrade the package
            #
            if {[catch {
                ns_log notice "===== INSTALL $package_key"
                set version_id [apm_package_install \
                        -enable=1 \
                        -load_data_model \
                        -data_model_files $data_model_files \
                        $spec_file]
                #
                # Upgrade successful
                #
                ns_log notice "===== INSTALL $package_key DONE"
                ns_write "Ok\n"
            } errorMsg]} {
                #
                # Upgrade failed
                #
                ns_write "Error\n"
                set all_fine false
                ns_log Error "ci-packages-upgrade: $package_key $errorMsg\n [ns_quotehtml $::errorInfo]"
            }
        }
    } else {
        #
        # Dependency check error
        #
        ns_write "* Dependency check failed\n"
        set all_fine false
    }
} else {
    #
    # Nothing to do
    #
    ns_write "* No upgrades required\n"
}
#
# All fine?
#
ns_write "* Done!\n"
if {$all_fine} {
    #
    # Grepped by CI pipeline to check if all upgrades were successful.
    #
    ns_log Warning "ci-packages-upgrade: all packages up-to-date."
} else {
    ns_log Warning "ci-packages-upgrade: upgrade failed."
    ns_write "=========================\n"
    ns_write "* Errors where found during package upgrade. Please check the error.log for details.\n"
}