Last active
March 18, 2024 13:01
-
-
Save NDiiong/b113dbbc474e0c758df19d6394953a74 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Cronicle Auto Installer | |
// Copyright (c) 2015 - 2023 Joseph Huckaby, MIT License. | |
// https://github.com/jhuckaby/Cronicle | |
// To install, issue this command as root: | |
// curl -s "https://raw.githubusercontent.com/jhuckaby/Cronicle/master/bin/install.js" | node | |
var path = require('path'); | |
var fs = require('fs'); | |
var util = require('util'); | |
var os = require('os'); | |
var cp = require('child_process'); | |
var installer_version = '1.5'; | |
var base_dir = '/opt/cronicle'; | |
var log_dir = base_dir + '/logs'; | |
var log_file = ''; | |
var gh_repo_url = 'http://github.com/jhuckaby/Cronicle'; | |
var gh_releases_url = 'https://api.github.com/repos/jhuckaby/Cronicle/releases'; | |
var gh_head_tarball_url = 'https://github.com/jhuckaby/Cronicle/archive/master.tar.gz'; | |
// don't allow npm to delete these (ugh) | |
var packages_to_check = ['couchbase', 'redis', 'ioredis', 'ioredis-timeout', 'sqlite3']; | |
var packages_to_rescue = {}; | |
// Error out if Node.js version is old | |
if (process.version.match(/^v?(\d+)/) && (parseInt(RegExp.$1) < 16) && !process.env['CRONICLE_OLD']) { | |
console.error("\nERROR: You are using an incompatible version of Node.js (" + process.version + "). Please upgrade to v16 or later. Instructions: https://nodejs.org/en/download/package-manager\n\nTo ignore this error and run unsafely, set a CRONICLE_OLD environment variable. Do this at your own risk.\n"); | |
process.exit(1); | |
} | |
var restore_packages = function() { | |
// restore packages that npm killed during upgrade | |
var cmd = "npm install"; | |
var num_found = 0; | |
for (var pkg in packages_to_rescue) { | |
cmd += ' ' + pkg + '@' + packages_to_rescue[pkg]; | |
num_found++; | |
} | |
if (!num_found) return; // nothing to restore | |
if (log_file) { | |
fs.appendFileSync(log_file, "\nExecuting npm command to restore lost packages: " + cmd + "\n"); | |
cmd += ' >>' + log_file + ' 2>&1'; | |
} | |
cp.execSync(cmd); | |
}; | |
var print = function(msg) { | |
process.stdout.write(msg); | |
if (log_file) fs.appendFileSync(log_file, msg); | |
}; | |
var warn = function(msg) { | |
process.stderr.write(msg); | |
if (log_file) fs.appendFileSync(log_file, msg); | |
}; | |
var die = function(msg) { | |
warn( "\nERROR: " + msg.trim() + "\n\n" ); | |
process.exit(1); | |
}; | |
var logonly = function(msg) { | |
if (log_file) fs.appendFileSync(log_file, msg); | |
}; | |
// create base and log directories | |
try { cp.execSync( "mkdir -p " + base_dir + " && chmod 775 " + base_dir ); } | |
catch (err) { die("Failed to create base directory: " + base_dir + ": " + err); } | |
try { cp.execSync( "mkdir -p " + log_dir + " && chmod 777 " + log_dir ); } | |
catch (err) { die("Failed to create log directory: " + log_dir + ": " + err); } | |
// start logging from this point onward | |
log_file = log_dir + '/install.log'; | |
logonly( "\nStarting install run: " + (new Date()).toString() + "\n" ); | |
print( | |
"\nCronicle Installer v" + installer_version + "\n" + | |
"Copyright (c) 2015 - 2022 PixlCore.com. MIT Licensed.\n" + | |
"Log File: " + log_file + "\n\n" | |
); | |
process.chdir( base_dir ); | |
var is_preinstalled = false; | |
var cur_version = ''; | |
var new_version = process.argv[2] || ''; | |
try { | |
var stats = fs.statSync( base_dir + '/package.json' ); | |
var json = require( base_dir + '/package.json' ); | |
if (json && json.version) { | |
cur_version = json.version; | |
is_preinstalled = true; | |
} | |
} | |
catch (err) {;} | |
var is_running = false; | |
if (is_preinstalled) { | |
var pid_file = log_dir + '/cronicled.pid'; | |
try { | |
var pid = fs.readFileSync(pid_file, { encoding: 'utf8' }); | |
is_running = process.kill( pid, 0 ); | |
} | |
catch (err) {;} | |
} | |
print( "Fetching release list...\n"); | |
logonly( "Releases URL: " + gh_releases_url + "\n" ); | |
cp.exec('curl -s ' + gh_releases_url, function (err, stdout, stderr) { | |
if (err) { | |
print( stdout.toString() ); | |
warn( stderr.toString() ); | |
die("Failed to fetch release list: " + gh_releases_url + ": " + err); | |
} | |
var releases = null; | |
try { releases = JSON.parse( stdout.toString() ); } | |
catch (err) { | |
die("Failed to parse JSON from GitHub: " + gh_releases_url + ": " + err); | |
} | |
// util.isArray is DEPRECATED??? Nooooooooode! | |
var isArray = Array.isArray || util.isArray; | |
if (!isArray(releases)) die("Unexpected response from GitHub Releases API: " + gh_releases_url + ": Not an array"); | |
var release = null; | |
for (var idx = 0, len = releases.length; idx < len; idx++) { | |
var rel = releases[idx]; | |
var ver = rel.tag_name.replace(/^\D+/, ''); | |
rel.version = ver; | |
if (!new_version || (ver == new_version)) { | |
release = rel; | |
new_version = ver; | |
idx = len; | |
} | |
} // foreach release | |
if (!release) { | |
// no release found -- use HEAD rev? | |
if (!new_version || new_version.match(/HEAD/i)) { | |
release = { | |
version: 'HEAD', | |
tarball_url: gh_head_tarball_url | |
}; | |
} | |
else { | |
die("Release not found: " + new_version); | |
} | |
} | |
// sanity check | |
if (is_preinstalled && (cur_version == new_version)) { | |
if (process.argv[2]) print( "\nVersion " + cur_version + " is already installed.\n\n" ); | |
else print( "\nVersion " + cur_version + " is already installed, and is the latest.\n\n" ); | |
process.exit(0); | |
} | |
// proceed with installation | |
if (is_preinstalled) print("Upgrading Cronicle from v"+cur_version+" to v"+new_version+"...\n"); | |
else print("Installing Cronicle v"+new_version+"...\n"); | |
if (is_running) { | |
print("\n"); | |
try { cp.execSync( base_dir + "/bin/control.sh stop", { stdio: 'inherit' } ); } | |
catch (err) { die("Failed to stop Cronicle: " + err); } | |
print("\n"); | |
} | |
// download tarball and expand into current directory | |
var tarball_url = release.tarball_url; | |
logonly( "Tarball URL: " + tarball_url + "\n" ); | |
cp.exec('curl -L ' + tarball_url + ' | tar zxf - --strip-components 1', function (err, stdout, stderr) { | |
if (err) { | |
print( stdout.toString() ); | |
warn( stderr.toString() ); | |
die("Failed to download release: " + tarball_url + ": " + err); | |
} | |
else { | |
logonly( stdout.toString() + stderr.toString() ); | |
} | |
try { | |
var stats = fs.statSync( base_dir + '/package.json' ); | |
var json = require( base_dir + '/package.json' ); | |
} | |
catch (err) { | |
die("Failed to download package: " + tarball_url + ": " + err); | |
} | |
print( is_preinstalled ? "Updating dependencies...\n" : "Installing dependencies...\n"); | |
var npm_cmd = is_preinstalled ? "npm update --unsafe-perm" : "npm install --unsafe-perm"; | |
logonly( "Executing command: " + npm_cmd + "\n" ); | |
// temporarily stash add-on modules that were installed separately (thanks npm) | |
if (is_preinstalled) packages_to_check.forEach( function(pkg) { | |
if (fs.existsSync('node_modules/' + pkg)) { | |
packages_to_rescue[pkg] = JSON.parse( fs.readFileSync('node_modules/' + pkg + '/package.json', 'utf8') ).version; | |
} | |
}); | |
// install dependencies via npm | |
cp.exec(npm_cmd, function (err, stdout, stderr) { | |
if (err) { | |
print( stdout.toString() ); | |
warn( stderr.toString() ); | |
if (is_preinstalled) restore_packages(); | |
die("Failed to install dependencies: " + err); | |
} | |
else { | |
logonly( stdout.toString() + stderr.toString() ); | |
} | |
print("Running post-install script...\n"); | |
logonly( "Executing command: node bin/build.js dist\n" ); | |
// finally, run postinstall script | |
cp.exec('node bin/build.js dist', function (err, stdout, stderr) { | |
if (is_preinstalled) { | |
// for upgrades only print output on error | |
if (err) { | |
print( stdout.toString() ); | |
warn( stderr.toString() ); | |
if (is_preinstalled) restore_packages(); | |
die("Failed to run post-install: " + err); | |
} | |
else { | |
if (is_preinstalled) restore_packages(); | |
print("Upgrade complete.\n\n"); | |
if (is_running) { | |
try { cp.execSync( base_dir + "/bin/control.sh start", { stdio: 'inherit' } ); } | |
catch (err) { die("Failed to start Cronicle: " + err); } | |
print("\n"); | |
} | |
} | |
} // upgrade | |
else { | |
// first time install, always print output | |
print( stdout.toString() ); | |
warn( stderr.toString() ); | |
if (err) { | |
die("Failed to run post-install: " + err); | |
} | |
else { | |
print("Installation complete.\n\n"); | |
} | |
} // first install | |
logonly( "Completed install run: " + (new Date()).toString() + "\n" ); | |
process.exit(0); | |
} ); // build.js | |
} ); // npm | |
} ); // download | |
} ); // releases api |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment