Skip to content

Commit

Permalink
add simple package management for nodes with binary dependencies (#170)
Browse files Browse the repository at this point in the history
  • Loading branch information
hobbyquaker committed Mar 24, 2019
1 parent 1d60ff9 commit 6532f1f
Show file tree
Hide file tree
Showing 9 changed files with 311 additions and 4 deletions.
60 changes: 60 additions & 0 deletions addon_files/redmatic/bin/redmatic-pkg
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/bin/sh

ADDON_DIR=/usr/local/addons/redmatic
REPO=$ADDON_DIR/lib/pkg-repo.json

source $ADDON_DIR/home/.profile

function usage()
{
echo "Usage: redmatic-pkg <command> <package>"
echo "commands:"
echo " install"
echo " remove"
echo ""
}

function install()
{
PACKAGE="$1"
URL=`jq -r ".\"$PACKAGE\".resolved" $REPO`
INTEGRITY=`jq -r ".\"$PACKAGE\".integrity" $REPO`
VERSION=`jq -r ".\"$PACKAGE\".version" $REPO`

if [ $URL == "null" ]; then
echo "unknown package: $1"
exit 1
fi

echo "Get $URL"
curl $URL -o $ADDON_DIR/tmp/$1.tar.gz --fail --silent --show-error --location || exit 1

rm -r $ADDON_DIR/lib/node_modules/$PACKAGE 2> /dev/null

echo "Extracting $1.tar.gz"
(cd $ADDON_DIR ; tar --no-same-owner -xzf tmp/$1.tar.gz && rm tmp/$1.tar.gz && echo "Done.")
}

function remove()
{
PACKAGE="$1"
rm -r $ADDON_DIR/lib/node_modules/$PACKAGE 2> /dev/null
echo "Done."
}


case $1 in
i | install)
install $2
exit
;;
r | remove)
remove $2
exit
;;
*)
usage
exit 1
;;
esac

37 changes: 37 additions & 0 deletions addon_files/redmatic/lib/pkg-repo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"node-red-node-serialport": {
"integrity": "sha512-NGTeA2zl9H9agWHDQAjxLQfNuyRhKjBcBLdY6xt9/7HM1sHiMzy/5fnX1ZjGNfyYpNrbBnftPqJKLiaTuSQQrw==",
"resolved": "https://github.com/rdmtc/RedMatic/releases/download/v3.3.2/redmatic-pkg-node-red-node-serialport-0.7.1.tar.gz",
"version": "0.7.1"
},
"node-red-node-sqlite": {
"integrity": "sha512-oufmJNdYH7nvLO54FtnpHySWIxnXmnsB7+sj6swqSZqxxyeFLjvaX0VLKDf6CqDWUoT+2tQ8ZwzWdRXemZfJ7w==",
"resolved": "https://github.com/rdmtc/RedMatic/releases/download/v3.3.2/redmatic-pkg-node-red-node-sqlite-0.3.6.tar.gz",
"version": "0.3.6"
},
"node-red-contrib-smartmeter": {
"integrity": "sha512-z4XUte5D0T6fYd2Waa53gEJgVOn63LXPpYY2CKDQ7zLoRcX7uxe9BxpHVQLujIVk++1dCTeyW6L8B+ftE5LSVQ==",
"resolved": "https://github.com/rdmtc/RedMatic/releases/download/v3.3.2/redmatic-pkg-node-red-contrib-smartmeter-0.2.0.tar.gz",
"version": "0.2.0"
},
"node-red-contrib-modbus": {
"integrity": "sha512-nRU3i+3COOCblviVqDO+FU44HALECihz4mt6K1rzhrWz0pzY18PN4cmVtezU02lVV1x32wmMNeQoQ88uvNncDg==",
"resolved": "https://github.com/rdmtc/RedMatic/releases/download/v3.3.2/redmatic-pkg-node-red-contrib-modbus-4.1.3.tar.gz",
"version": "4.1.3"
},
"node-red-contrib-mysensors": {
"integrity": "sha512-4fY9TO5aDtafcQDakbWG0WPkT3KlDs0QCG6GaHZdZRmKdfFWR/k6jXcqWVaM7t0paqhl4AVDet4qdtdBME1ZVA==",
"resolved": "https://github.com/rdmtc/RedMatic/releases/download/v3.3.2/redmatic-pkg-node-red-contrib-mysensors-3.3.0.tar.gz",
"version": "3.3.0"
},
"node-red-contrib-rfxcom": {
"integrity": "sha512-ewgyopSGtXJunxFdaI6egs6zAJtR3BUQ25/Oi2Hzh99NLCNSza+pnTElbchew+/G0NYkpS5keF2xhzyki5qw7A==",
"resolved": "https://github.com/rdmtc/RedMatic/releases/download/v3.3.2/redmatic-pkg-node-red-contrib-rfxcom-2.8.1.tar.gz",
"version": "2.8.1"
},
"redmatic-homekit": {
"integrity": "sha512-8ZXMcreZVabftjuV0FpXKFFPl92kfQzPhptGlNhvHWPYNrQB16FHjorsaa/9I/ARIsqmZ5SEYWqc84eJpYhq/g==",
"resolved": "https://github.com/rdmtc/RedMatic/releases/download/v3.3.2/redmatic-pkg-redmatic-homekit-2.1.0.tar.gz",
"version": "2.1.0"
}
}
16 changes: 16 additions & 0 deletions addon_files/redmatic/www/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,20 @@ iframe {

.status-stopped {
color: red;
}

.spinner-placeholder {
display: inline-block;
margin: 0;
padding: 0;
width: 18px;
height: 18px;
}

.spinner-install, .spinner-remove {
display: inline-block;
}

.pkg-remove, .pkg-install {
width: 100px;
}
50 changes: 50 additions & 0 deletions addon_files/redmatic/www/js/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ $(document).ready(() => {
const $linkRed = $('#link-red');
const $linkUi = $('#link-ui');

const $packageTable = $('#package-table');

let config;

const qs = location.search;
Expand All @@ -67,6 +69,54 @@ $(document).ready(() => {
let psTimeout;
let psInterval = 5000;

function pkg() {
let packages;
$.get(`pkg.cgi?sid=${sid}&cmd=repo`, data => {
packages = data;
$.get(`pkg.cgi?sid=${sid}&cmd=ls`, data => {
data.split('\n').forEach(line => {
if (line && packages[line]) {
packages[line].installed = true;
}
});
$packageTable.html('');
Object.keys(packages).forEach(name => {
$packageTable.append(`<tr><td>${name}</td><td>${packages[name].version}</td><td>${packages[name].installed ? '✓' : ''}</td><td><button data-pkg="${name}" type="button" class="btn btn-primary btn-sm pkg-install" ${packages[name].installed ? 'disabled' : ''}><span class="spinner-install spinner-border spinner-border-sm" role="status" aria-hidden="true" hidden></span>
install</button> <button data-pkg="${name}" type="button" class="btn btn-danger btn-sm pkg-remove" ${packages[name].installed ? '' : 'disabled'}><span class="spinner-remove spinner-border spinner-border-sm" role="status" aria-hidden="true" hidden></span>
remove</button></td></tr>`)
});
$('.pkg-install').click(function () {
$(this).attr('disabled', true);
$(this).find('.spinner-install').removeAttr('hidden');
$.get(`pkg.cgi?sid=${sid}&cmd=install&package=${$(this).data('pkg')}`, (data, success) => {
$(this).find('.spinner-install').attr('hidden', true);
if (data.includes('Done.')) {
alert($alertExec);
} else {
alert($alertError);
}
pkg();
});
});
$('.pkg-remove').click(function () {
$(this).attr('disabled', true);
$(this).find('.spinner-remove').removeAttr('hidden');
$.get(`pkg.cgi?sid=${sid}&cmd=remove&package=${$(this).data('pkg')}`, (data, success) => {
$(this).find('.spinner-remove').attr('hidden', true);
if (data.includes('Done.')) {
alert($alertExec);
} else {
alert($alertError);
}
pkg();
});
})
});
});
}

pkg();

function cpu() {
$.get(`service.cgi?sid=${sid}&cmd=cpu`, (data, success) => {
$cpu.html(data ? 'cpu ' + data : '');
Expand Down
47 changes: 47 additions & 0 deletions addon_files/redmatic/www/pkg.cgi
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/bin/tclsh

source ../lib/querystring.tcl
source ../lib/session.tcl


if {[info exists sid] && [check_session $sid]} {
if {[info exists cmd]} {
switch -glob $cmd {
"repo" {
puts -nonewline "Content-Type: text/json; charset=utf-8\r\n\r\n"
puts [exec cat /usr/local/addons/redmatic/lib/pkg-repo.json]
}
"ls" {
puts -nonewline "Content-Type: text/plain; charset=utf-8\r\n\r\n"
puts [exec ls -1 /usr/local/addons/redmatic/lib/node_modules]
}
"install" {
puts -nonewline "Content-Type: text/plain; charset=utf-8\r\n\r\n"
if {[info exists package]} {
puts [exec /usr/local/addons/redmatic/bin/redmatic-pkg install $package]
} else {
puts "param package missing"
}
}
"remove" {
puts -nonewline "Content-Type: text/plain; charset=utf-8\r\n\r\n"
if {[info exists package]} {
puts [exec /usr/local/addons/redmatic/bin/redmatic-pkg remove $package]
} else {
puts "param package missing"
}
}
default {
puts -nonewline "Content-Type: text/plain; charset=utf-8\r\n\r\n"
puts "unknown command $cmd"
}
}
} else {
puts -nonewline "Content-Type: text/plain; charset=utf-8\r\n\r\n"
puts "param cmd missing"
}

} else {
puts -nonewline "Content-Type: text/plain; charset=utf-8\r\n\r\n"
puts "error: invalid session"
}
26 changes: 22 additions & 4 deletions addon_files/redmatic/www/settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
<li class="nav-item ml-3">
<a class="nav-link active pb-0" id="configuration-tab" data-toggle="tab" role="tab" aria-controls="configuration" aria-selected href="#configuration">Configuration</a>
</li>
<li class="nav-item ml-3">
<a class="nav-link pb-0" id="packages-tab" data-toggle="tab" role="tab" aria-controls="packages" aria-selected href="#packages">Packages</a>
</li>
<li class="nav-item ml-3">
<a class="nav-link pb-0" id="versions-tab" data-toggle="tab" role="tab" aria-controls="versions" aria-selected href="#versions">Debug</a>
</li>
Expand All @@ -43,7 +46,6 @@

<div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="configuration" role="tabpanel" aria-labelledby="configuration-tab">

<div class="card mb-4">
<div class="card-body">
<a id="link-red" role="button" class="btn btn-sm btn-outline-info" target="_blank" href="/addons/red/"><i class="fa fa-external-link-alt"></i> Node-RED</a>
Expand Down Expand Up @@ -81,9 +83,6 @@
</div>
</div>
</div>



<div class="card mb-4 pr-4">
<div class="card-body">
<div class="form-group">
Expand Down Expand Up @@ -233,6 +232,25 @@

</div>

<div class="tab-pane fade show" id="packages" role="tabpanel" aria-labelledby="packages-tab">

<table class="table">
<thead>
<tr>
<th scope="col">Package</th>
<th scope="col">Version</th>
<th scope="col">Installed</th>
<th scope="col"></th>
<th scope="col"></th>
</tr>
</thead>
<tbody id="package-table">

</tbody>
</table>

</div>

<div class="tab-pane fade show" id="versions" role="tabpanel" aria-labelledby="versions-tab">

<div class="card mb-4">
Expand Down
13 changes: 13 additions & 0 deletions addon_files/update_script
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ fi
#
cp -af redmatic $ADDONS_DIR/


#
# Create default config on first install
#
Expand Down Expand Up @@ -109,6 +110,18 @@ if [ -d $RED_DIR/var/node_modules/redmatic-homekit ]; then
rm $RED_DIR/var/package.json.bak
fi


#
# Update existing modules in lib/node_modules on update
#
echo $(date +'[%Y-%m-%d %H:%M:%S]') > $RED_DIR/var/pkg-update.log
for k in $($RED_DIR/bin/jq -r '. | to_entries[] | "\(.key)"' $RED_DIR/lib/pkg-repo.json); do
if [ -d $RED_DIR/lib/node_modules/$k ]; then
$RED_DIR/bin/redmatic-pkg install $k >> $RED_DIR/var/pkg-update.log 2>&1
fi
done


#
# Remove package-lock
#
Expand Down
3 changes: 3 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ cp -r $PREBUILT/* $ADDON_TMP/redmatic/
cd $ADDON_TMP
ln -s redmatic/bin/update_addon ./

echo "bundling packages..."
node $BUILD_DIR/bundle-pkgs.js

echo "installing www node modules"
cd $ADDON_TMP/redmatic/www
npm install --silent --no-package-lock --production --no-optional
Expand Down
63 changes: 63 additions & 0 deletions bundle-pkgs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
const fs = require('fs');
const cp = require('child_process');
var crypto = require('crypto');
const pkgLib = require(__dirname + '/addon_files/redmatic/lib/package.json');
const redmaticVersion = require(__dirname + '/package.json').version;

const blacklist = [
'node-red',
'npm',
'ain2'
];

const extraFiles = {
'redmatic-homekit': [
'bin/ffmpeg'
]
};

const remove = [];
const repo = {};

Object.keys(pkgLib.dependencies).forEach(name => {
if (blacklist.includes(name)) {
return;
}
const version = pkgLib.dependencies[name]
const filename = 'redmatic-pkg-' + name + '-' + version + '.tar.gz';

let cmd = 'tar -C ' + __dirname + '/addon_tmp/redmatic/ -czf ' + __dirname + '/dist/' + filename + ' lib/node_modules/' + name;
remove.push(__dirname + '/addon_tmp/redmatic/lib/node_modules/' + name);
if (extraFiles[name]) {
extraFiles[name].forEach(file => {
cmd += ' ' + file;
remove.push(__dirname + '/addon_tmp/redmatic/' + file);
});
}
console.log(cmd);
cp.execSync(cmd);

repo[name] = {
integrity: checksum(fs.readFileSync(__dirname + '/dist/' + filename)),
resolved: 'https://github.com/rdmtc/RedMatic/releases/download/v' + redmaticVersion + '/' + filename,
version
};

});

fs.writeFileSync(__dirname + '/addon_tmp/redmatic/lib/pkg-repo.json', JSON.stringify(repo, null, ' '));

remove.forEach(path => {
console.log('remove', path);
if (fs.statSync(path).isDirectory()) {
cp.execSync('rm -r ' + path);
} else {
fs.unlinkSync(path);
}
});

function checksum(input) {
var hash = crypto.createHash('sha512').update(input, 'utf8');
var hashBase64 = hash.digest('base64');
return 'sha512-' + hashBase64;
}

0 comments on commit 6532f1f

Please sign in to comment.