Process output from 'npm outdated' command
This example flow shows you how to make use of the npm outdated
command using the exec node.
UPDATE August 2024: Please see the Process output from ‘npm outdated’ command thread on the Node-RED forum for some more modern approaches to this.
It processes the data, saves it to a global variable and outputs to a link node so that you can use it elsewhere (I send that to MQTT for example).
The output is structured so that you can use if for further processing or simply for alerting you when there are things that need updating.
You will want to make changes to the flow for your own requirements. You can add/remove the commands, the topic names and make various changes in the function node.
There is nothing destructive in the flow so feel free to play around with it.
For reference, here is the code from the function node:
/**
* Process the output from an `npm outdated` command.
* Use msg.topic to allow different project folders to be tracked.
* e.g. Global (`npm outdated -g`), 'user' for the Node-RED userDir, etc.
*/
// End-of-line characters are different on each Operating System
// This uses Node.js features to get the right one but you
// can simply replace all of this with the appropriate string of control chars.
const require = global.get('require') // You have to add this to globals in settings.js
const EOL = require('os').EOL // OR just add `os.EOL` to settings.js instead and get that
// Keep everything together - change this to flow or global if you want
const outdated = global.get('npm_outdated') || {}
// reset on each run - use the date entry if you want to track that
outdated[msg.topic] = {} // {updated: new Date()}
let out = outdated[msg.topic]
let heads = []
// Split the lines and process each one
msg.payload.split(EOL).forEach( (line,idx) => {
// Don't do anything for empty lines
if (line.length>0) {
// Split the line by spaces, get rid of any entries that were just a space
let tmp = line.split(' ').filter(e => e.length>0)
if ( idx === 0 ) {
// Track the headings that are on line 1
heads = tmp
} else {
// Create an object for each package that needs updating
out[tmp[0]] = {}
tmp.forEach( (d,i) => {
// and record the details
out[tmp[0]][heads[i]] = d
})
}
}
})
// Save - don't forget to change to global/flow if you change the get
global.set('npm_outdated', outdated)
return [
// Output just this entry
{
topic: `npm/outdated/${msg.topic}`,
parts: msg.parts,
payload: out,
},
// Output everything
{
topic: `npm/outdated`,
parts: msg.parts,
payload: outdated,
},
]
[{"id":"db49feb7.ebd85","type":"group","z":"956152e4.d7617","name":"Output results of `npm outdated` commands - what needs updating? Runs on startup then at 6am every sunday \\n ","style":{"label":true},"nodes":["687479a3.b02a18","e3d6de9d.7c244","126ee251.c4565e","907a2f34.8ab6a","ce249e80.acf79","c04b00db.601b5","fe944ceb.23bd","d40304c4.13a8c8","c92c5229.6212","2348d03a.9fbac","bdd3cf8f.28bbf","a7131e8d.e4adc"],"x":34,"y":1655.5,"w":1052,"h":233},{"id":"687479a3.b02a18","type":"inject","z":"956152e4.d7617","g":"db49feb7.ebd85","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"00 08 * * 0","once":false,"onceDelay":"5","topic":"","payload":"","payloadType":"date","x":150,"y":1780,"wires":[["e3d6de9d.7c244","126ee251.c4565e","ce249e80.acf79"]]},{"id":"e3d6de9d.7c244","type":"exec","z":"956152e4.d7617","g":"db49feb7.ebd85","command":"npm outdated -g","addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"","x":360,"y":1720,"wires":[["d40304c4.13a8c8"],[],[]]},{"id":"126ee251.c4565e","type":"exec","z":"956152e4.d7617","g":"db49feb7.ebd85","command":"cd ~/nrmain && npm outdated","addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"","x":410,"y":1780,"wires":[["c92c5229.6212"],[],[]]},{"id":"907a2f34.8ab6a","type":"debug","z":"956152e4.d7617","d":true,"g":"db49feb7.ebd85","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1015,"y":1760,"wires":[],"l":false},{"id":"ce249e80.acf79","type":"exec","z":"956152e4.d7617","g":"db49feb7.ebd85","command":"cd ~/nrmain/data && npm outdated","addpay":false,"append":"","useSpawn":"false","timer":"","oldrc":false,"name":"","x":420,"y":1840,"wires":[["fe944ceb.23bd"],[],[]]},{"id":"c04b00db.601b5","type":"function","z":"956152e4.d7617","g":"db49feb7.ebd85","name":"","func":"/**\n * Process the output from an `npm outdated` command.\n * Use msg.topic to allow different project folders to be tracked.\n * e.g. Global (`npm outdated -g`), 'user' for the Node-RED userDir, etc.\n */\n \n// End-of-line characters are different on each Operating System\n// This uses Node.js features to get the right one but you\n// can simply replace all of this with the appropriate string of control chars.\nconst require = global.get('require') // You have to add this to globals in settings.js\nconst EOL = require('os').EOL // OR just add `os.EOL` to settings.js instead and get that\n\n// Keep everything together - change this to flow or global if you want\nconst outdated = global.get('npm_outdated') || {}\n\n// reset on each run - use the date entry if you want to track that\noutdated[msg.topic] = {} // {updated: new Date()}\n\nlet out = outdated[msg.topic]\nlet heads = []\n\n// Split the lines and process each one\nmsg.payload.split(EOL).forEach( (line,idx) => {\n // Don't do anything for empty lines\n if (line.length>0) {\n // Split the line by spaces, get rid of any entries that were just a space\n let tmp = line.split(' ').filter(e => e.length>0)\n \n if ( idx === 0 ) {\n // Track the headings that are on line 1\n heads = tmp\n } else {\n // Create an object for each package that needs updating\n out[tmp[0]] = {}\n tmp.forEach( (d,i) => {\n // and record the details\n out[tmp[0]][heads[i]] = d\n })\n }\n }\n})\n\n// Save - don't forget to change to global/flow if you change the get\nglobal.set('npm_outdated', outdated)\n\nreturn [\n // Output just this entry\n {\n topic: `npm/outdated/${msg.topic}`,\n parts: msg.parts,\n payload: out,\n },\n // Output everything\n {\n topic: `npm/outdated`,\n parts: msg.parts,\n payload: outdated,\n },\n]","outputs":2,"noerr":0,"initialize":"","finalize":"","x":840,"y":1780,"wires":[["bdd3cf8f.28bbf","a7131e8d.e4adc"],["2348d03a.9fbac"]],"outputLabels":["sequence","All results"]},{"id":"fe944ceb.23bd","type":"change","z":"956152e4.d7617","g":"db49feb7.ebd85","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"user","tot":"str"},{"t":"set","p":"parts","pt":"msg","to":"{\"id\":2,\"index\":2,\"count\":3}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":660,"y":1840,"wires":[["c04b00db.601b5"]]},{"id":"d40304c4.13a8c8","type":"change","z":"956152e4.d7617","g":"db49feb7.ebd85","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"global","tot":"str"},{"t":"set","p":"parts","pt":"msg","to":"{\"id\":0,\"index\":0,\"count\":3}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":1720,"wires":[["c04b00db.601b5"]]},{"id":"c92c5229.6212","type":"change","z":"956152e4.d7617","g":"db49feb7.ebd85","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"master","tot":"str"},{"t":"set","p":"parts","pt":"msg","to":"{\"id\":1,\"index\":1,\"count\":3}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":640,"y":1780,"wires":[["c04b00db.601b5"]]},{"id":"2348d03a.9fbac","type":"debug","z":"956152e4.d7617","d":true,"g":"db49feb7.ebd85","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":990,"y":1820,"wires":[]},{"id":"bdd3cf8f.28bbf","type":"join","z":"956152e4.d7617","d":true,"g":"db49feb7.ebd85","name":"","mode":"custom","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":true,"timeout":"","count":"3","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"num","reduceFixup":"","x":955,"y":1760,"wires":[["907a2f34.8ab6a"]],"l":false},{"id":"a7131e8d.e4adc","type":"link out","z":"956152e4.d7617","g":"db49feb7.ebd85","name":"npm_outdated ->","links":["6e2ccf41.879f4"],"x":955,"y":1720,"wires":[]}]