Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
The Bleeding Edge Toolkit for unRAID. Allows you update your system with the latest unreleased webui code.
ini_set('display_errors', '1');
bleeding_edge_toolkit Copyright 2018-2019, ljm42
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2,
as published by the Free Software Foundation.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
the latest version of this script can be found here:
### ABOUT ###
This script lets you apply patches to your unRAID webui directly from GitHub.
You specify which patches to install, and this will reinstall them on every reboot.
This is not provided as a full solution. You need to modify the code below to download and install the specific patches you want.
More caveats:
* Uses the patch command to process GitHub patches (both PRs and commits)
* Automatically downloads binary files (such as images), which the patch command does not process
* Guards against running the same patch twice
* Sets appropriate permissions (as specified in the patch) on new files
* Logs results in /tmp/bleeding_edge_toolkit/log-[datetime].txt for later review
* Install the User Scripts plugin in unRAID, then Add New Script called "bleeding_edge_toolkit" and paste this script there.
* Modify the script to install the patches you want.
* Run the script manually until you are happy with your modifications, then schedule it to run at "first array start only"
* Note: to "uninstall" a patch, comment it out below and then reboot.
TODO: request way to have this run at boot instead of first array start
// pass in the message to be logged
// this will "echo" the message, and append it to the $logHandle
// note: opening/closing the $logHandle is done elsewhere
function logger ($msg)
global $logHandle;
echo $msg.PHP_EOL;
if (is_resource($logHandle)) { fwrite($logHandle, $msg.PHP_EOL); }
// pass in a .patch url (either a PR or a commit) from a github webui repo
// this will process the results with the "patch" command, download or delete binary files, and fix permissions on new files
function applyPatch ($remote)
global $emhttpDir, $patchDir;
preg_match('@([^/]*)/([^/]*)/(pull|commit)/([^\.]*)(\.patch)@', $remote, $remoteParts);
if (!$remoteParts) {
logger("invalid patch url, expecting link to PR or commit, followed by .patch ( {$remote} )");
$patchFile = $patchDir.str_replace('/', '_', str_replace(':', '_', $remote));
if (file_exists($patchFile)) { logger("patch already applied ( {$remote} )"); return; }
// download the patch file
$contentArr = @file($remote);
if ($contentArr) {
file_put_contents($patchFile, implode("", $contentArr));
} else {
logger("unable to download ( {$remote} )"); return;
// loop through patchFile, generate array of files changed and which commit they were changed in
// the commit string is needed to download binary files
$filesArr = [];
$thisCommit = "";
foreach ($contentArr as $line) {
// From 04ec6d8cc825250d4a4926cc7478bdb571521b31 Mon Sep 17 00:00:00 2001
preg_match('@From ([^ ]*) (... ... .. ..:..:.. ....)@', $line, $matches1);
if ($matches1) {
$thisCommit = $matches1[1];
// diff --git a/plugins/dynamix.vm.manager/icons/addvm.png b/plugins/dynamix.vm.manager/icons/addvm.png
preg_match('@diff --git a/(.*) b/(.*)@', $line, $matches2);
if ($matches2) {
$thisFile = $matches2[1];
$filesArr[$thisFile] = $thisCommit;
// apply the patch file, then process each line of the output
logger("about to apply patch ( {$remote} )");
$execCmd = "patch -p 1 -d \"{$emhttpDir}\" -i \"{$patchFile}\"";
exec($execCmd, $execOutputArr, $execReturn);
foreach ($execOutputArr as $outputLine) {
// handle binary files
preg_match('@File (.*): git binary diffs are not supported.@', $outputLine, $matches);
if ($matches) {
$file = $matches[1];
$commit = $filesArr[$file];
// binary downloads in this format:
$binaryUrlRoot = $remoteParts[1].'/'.$remoteParts[2].'/'.$remoteParts[3].'/raw/'.$commit.'/';
downloadOrDelete($binaryUrlRoot, $file);
} else {
// just output the line
logger("- {$outputLine}");
// set perms on new files as specified by patch file
$newFiles = preg_grep("/^ create mode /", $contentArr);
foreach ($newFiles as $newFile) {
preg_match('@ create mode 100([4567][4567][4567]) (.*)@', $newFile, $matches);
if ($matches) {
$mode = $matches[1];
$file = $matches[2];
$local = $emhttpDir.$file;
if (@chmod ($local, octdec($mode))) {
logger("--- set perms $mode on $file");
} else {
logger("--- unable to set perms $mode on $file");
} else {
logger("--- unexpected mode ( {$newFile} )");
// called by applyPatch() to process binary files
// if $file exists at the $binaryUrlRoot url provided, it will be downloaded and placed on the $local file system
// if the $file does not exist at the $binaryUrlRoot url provided, the $local version will be deleted
function downloadOrDelete($binaryUrlRoot, $file)
global $emhttpDir;
$local = $emhttpDir.$file;
$remote = $binaryUrlRoot.$file;
$content = @file_get_contents($remote);
if ($content) {
if (file_put_contents($local, $content) !== false) {
logger("-- replaced binary file $file");
} else {
logger("-- skipped binary file $file");
} else {
// file not at this url, assume this patch deleted it
logger("-- deleted binary file $file");
// begin script
$emhttpDir = '/usr/local/emhttp/';
$patchDir = '/tmp/bleeding_edge_toolkit/';
$unraid = parse_ini_file('/etc/unraid-version');
// open log file
@mkdir($patchDir, 0777, true);
$logFile = $patchDir.'log-'.@date('Ymd-His').'.txt';
$logHandle = fopen($logFile, 'a') or exit("Can't open $logFile".PHP_EOL);
logger("unRAID version {$unraid['version']}");
logger("logfile: $logFile");
// Begin customizations here
// Note that it is up to you to make sure the patches you install are compatible with your version of unRAID.
// The code below is just an example. You need to decide which patches you are comfortable installing.
// If a patch gives errors on merge, that means you need to install previous patches first
// Note that some webui patches require corresponding changes to the unRAID back end, and will not work until you upgrade
switch ($unraid['version'] ) {
case 'test':
// for some reason, github does not have a .patch file for this patch
// PR with new image file. patch can create it natively
// PR with binary patch (from another package)
case '6.7.0-rc3':
case '6.7.0-rc4':
$badfile = '/usr/local/emhttp/tics: dynamic file name creation';
if ( file_exists($badfile) ) {
logger("- deleted $badfile");
case '6.7.0-rc5':
logger("nothing to do for this version of unRAID");
// close log file
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment