Plugin update process

SeanM1
Tera Guru

Hi all,

 

Just have a question about what process people are doing to update plugins.

 

Upgrading plugins is easily the worse part of my ServiceNow experience, scheduling the update doesn't work when you have hundreds to deal with.

 

I've just updated to Xanadu and after making all the initial plugin updates due to the family instance change, I was shocked and frustrated a week later, I had another 250+ updates required (which needs to be completed in 3 separate instances) it's a very time consuming process.

 

I'm interested in hearing how other people are managing high volume plugin updates, maybe there's a better way that I'm missing.

 

Ideally, I would like the the ability to opt in to auto update these plugins, overnight between the hours I define and if they don't complete, continue the next night. 

1 ACCEPTED SOLUTION

Big thanks to @Eric Riemer 

/*----------------------------------------------------*/
/*                                                    */
/*  Have a bunch of apps that need to be updated?     */
/*  Run this and follow the directions in the output  */
/*  It will build a payload and use the CI/CD API to  */
/*  run a batch install of all of the needed updates. */
/*                                                    */
/*----------------------------------------------------*/

//Want Demo Data with the app?
var loadDemoData = true;


var prevName;
var appsArray = [];
var grSSA = new GlideRecord('sys_store_app');
grSSA.addEncodedQuery('install_dateISNOTEMPTY^hide_on_ui=false');
grSSA.orderBy('name');
grSSA.orderBy('version');
grSSA.query();
while (grSSA.next()) {
    var curName = grSSA.getValue('name');
    if (curName == prevName) {
        continue;
    }
    var installedVersion = grSSA.getValue('version');
    var latestVersion = grSSA.getValue('latest_version');
    if (latestVersion != installedVersion) {
        prevName = curName;
        var appObject = {
            displayName: curName,
            id: grSSA.getUniqueValue(),
            load_demo_data: loadDemoData,
            type: "application",
            requested_version: latestVersion
        };
        appsArray.push(appObject);
    }
}
var appsPackages = {};
appsPackages.packages = appsArray;
appsPackages.name = 'Update Apps';
var data = new global.JSON().encode(appsPackages);

var url = 'https://' + gs.getProperty('instance_name') + '.service-now.com/$restapi.do?ns=sn_cicd&service=CICD%20Batch%20Install%20API&version=latest';
gs.info('Open the following URL in a new tab:\n\n' + url + '\n\ncopy/paste the following JSON into the "Raw" Request body\n\n' + data);

View solution in original post

6 REPLIES 6

If I ever meet you or Eric in the wild the first beer is on me!! this worked great 

Lorenzo Persich
ServiceNow Employee
ServiceNow Employee

While the above script works fine, I stumbled upon some limitations/issues:

  • Simply comparing that "latestVersion != installedVersion" can lead to downgrades instead of upgrades in some situations. This is happening because the "grSSA.orderBy('version');" is not 100% reliable when version number is weird. (e.g. comparing 6.1.2 to 6.1.100)
  • There is one dedicated filed to check if upgrade is available or not
  • Taking the last available version can lead to incompatibilities. For example, the customer has version 6.3 and is running Yokohama. The latest version is 7.0, and it requires Zurich, while the previous version, 6.9, remains compatible with Yokohama.
  • Upgrading some system applications (mainly the ones beginning with @) can also lead to errors in the results
  • Creating one big batch can lead to issues and can create some overhead while managing related skipped records

 

Based on these limitations, I developed my own version of the script over time, which I am sharing here. Have a look at the variables on top to modify the behavior.

 

/*----------------------------------------------------*/
/*                                                    */
/*  Have a bunch of apps that need to be updated?     */
/*  Run this and follow the directions in the output  */
/*  It will build a payload and use the CI/CD API to  */
/*  run a batch install of all of the needed updates. */
/*                                                    */
/*----------------------------------------------------*/

// Want Demo Data with the app?
var loadDemoData = false;
var preserveDemoData = true;

// What is the maximum number of application to update
var appLimit = 50;

// Log prefix
var prefix = "[BATUCH PLUGIN UPGRADE SCRIPT]";

// Instance Build Name
var buildName = gs.getProperty('glide.buildname');

var prevName;
var appsArray = [];

// Limit reached
var limitReached = false;
var updateCnt = 0;

// The store_app encoded query
var query = "install_dateISNOTEMPTY^hide_on_ui=false^nameNOT LIKE@^update_available=true";

var grSSA = new GlideRecord('sys_store_app');
grSSA.addEncodedQuery(query);
grSSA.orderBy('name');
grSSA.orderBy('version');
grSSA.query();
while (grSSA.next()) {
    var curName = grSSA.getValue('name');
    var curScope = grSSA.getValue('scope');
    if (curName == prevName) {
        continue;
    }

    // app_version encoded query
    var versionQuery = "source_app_id=" + grSSA.getUniqueValue() + "^compatibilitiesLIKE" + buildName;
    
    var grAppVersion = new GlideRecord('sys_app_version');
    grAppVersion.addEncodedQuery(versionQuery);
    grAppVersion.setLimit(1);
    grAppVersion.orderByDesc('version');
    grAppVersion.query();
    while (grAppVersion.next()) {

        // https://developer.servicenow.com/dev.do#!/reference/api/zurich/rest/cicd-api#cicd-POST-app-batch-install
        var appObject = {
            id: grSSA.getUniqueValue(),
            load_demo_data: loadDemoData, // We initialize it with the default value
            // scope: curScope,
            displayName: curName,
            type: "application",
            requested_version: grAppVersion.getValue('version')
        };

        // If preserve logic is there, we apply the correct logic
        if (preserveDemoData) {
            var demoData = String(grSSA.getValue('demo_data'));
            appObject.load_demo_data = (demoData === 'demo_data_loaded');
        }

        if (appsArray.length < appLimit) {
            appsArray.push(appObject);
            updateCnt += 1;
        }
        else {
            limitReached = true;
            break;
        }
    }
}

if (appsArray.length > 0) {

    if (limitReached) {
        prefix += "\n\n!!!!!!!!!!!!ATTENTION - LIMIT OF " + appLimit + " HAS BEEN REACHED!!!!!!!!!!!!";
    }

    prefix += "\n\nA total of " + updateCnt + " plugins will be upgraded";

    var time = new GlideDateTime();
    var appsPackages = {};
    appsPackages.packages = appsArray;
    appsPackages.name = 'Batch Applications Update via CI/CD - ' + time;
    var data = JSON.stringify(appsPackages, null, " ");
    var url = 'https://' + gs.getProperty('instance_name') + '.service-now.com/$restapi.do?ns=sn_cicd&service=CICD%20Batch%20Install%20API&version=latest';
    gs.info(prefix + '\n\nOpen the following URL in a new tab:\n\n' + url + '\n\ncopy/paste the following JSON into the "Raw" Request body\n\n' + data);


} else {
    gs.info(prefix + " No application has been found");
}