Skip to content

Commit

Permalink
Merge pull request #45 from flydev-fr/feat-custom-script
Browse files Browse the repository at this point in the history
Feat custom script
  • Loading branch information
flydev-fr authored Oct 28, 2023
2 parents a2eacdd + 30c5143 commit 40cc5ed
Show file tree
Hide file tree
Showing 7 changed files with 293 additions and 71 deletions.
14 changes: 8 additions & 6 deletions .github/workflows/releases.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,6 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: akhilmhdh/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
commit_message: "docs(contributor): readme updated"

- uses: actions/checkout@v3
- name: conventional Changelog Action
id: changelog
Expand All @@ -31,3 +25,11 @@ jobs:
tag_name: ${{ steps.changelog.outputs.tag }}
release_name: ${{ steps.changelog.outputs.tag }}
body: ${{ steps.changelog.outputs.clean_changelog }}

- name: Contribute List
uses: akhilmhdh/[email protected]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
commit_message: "docs(contributor): contrib updated"

163 changes: 117 additions & 46 deletions Classes/BackupDatabase.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

class BackupDatabase
{
const DUP_MODE_PWAPI = 0;
const DUP_MODE_NATIVE = 1;

protected $database;
protected $options;
protected $mode;
Expand All @@ -13,6 +16,8 @@ class BackupDatabase

public function __construct(array $options = array())
{
$this->OS = DUP_Util::getOS();

$this->options = array(
'path' => '',
'backup' => array(
Expand All @@ -21,16 +26,12 @@ public function __construct(array $options = array())
'maxSeconds' => 120,
),
'cachePath' => wire('config')->paths->cache,
'chmodPermission' => '0700'
'chmodPermission' => '0700',
'shellScript' => '',
'zipbinary' => false,
);

$this->options = array_merge($this->options, $options);

if (DUP_Util::isWinOS()) {
$this->OS = 'WINDOWS';
} else {
$this->OS = 'UNIX';
}
}

public function setDatabase($database)
Expand All @@ -47,6 +48,33 @@ public function setMode($mode)
return $this;
}

// set default shell script from stub file for Windows OS (batch file) or Unix OS (shell script)
public function setDefaultShellScript($customScriptData = false)
{
$stub = '';
if ($customScriptData) {
$stub = $customScriptData;
} else {
$filename = DUP_Util::getStub($this->OS);
if (!$filename) return false;
$stub = file_get_contents($filename);
}

if ($stub) {
$stub = str_replace('%%FILE%%', $this->options['backup']['filename'], $stub);
$stub = str_replace('%%SERVER%%', wire('config')->dbHost, $stub);
$stub = str_replace('%%DATABASE%%', wire('config')->dbName, $stub);
$stub = str_replace('%%USER%%', wire('config')->dbUser, $stub);
$stub = str_replace('%%PASS%%', wire('config')->dbPass, $stub);
$stub = str_replace('%%CACHEPATH%%', $this->options['cachePath'], $stub);
}

// set shell script
$this->options['shellScript'] = $stub;

return $stub;
}

/*
* do backup using different mode
* return a SQL file
Expand All @@ -57,12 +85,12 @@ public function backup()
{
DUP_Logs::log("Backup Database");
switch ($this->mode) {
case 'MODE_PWAPI':
case self::DUP_MODE_PWAPI:
DUP_Logs::log("- Backup using standard mode");
return $this->fromProcessWire();
break;

case 'MODE_NATIVE':
case self::DUP_MODE_NATIVE:
DUP_Logs::log("- Backup using native tools");
return $this->fromNativeTools();
break;
Expand All @@ -82,25 +110,34 @@ public function getZip()
$sqlfile = $this->backup();
$this->size = filesize($sqlfile);
$zipfile = $this->options['path'] . $this->options['backup']['filename'] . '.zip';
if ($this->mode === 'MODE_PWAPI') {
if ($this->mode === self::DUP_MODE_PWAPI) {
$result = wireZipFile($zipfile, $sqlfile);
DUP_Util::deleteFile($sqlfile);
if (count($result['errors'])) {
foreach ($result['errors'] as $error) {
DUP_Logs::log("ZIP add failed: $error");
}
}
} else if ($this->mode === 'MODE_NATIVE') {
} else if ($this->mode === self::DUP_MODE_NATIVE) {
$return = null;
$output = array();

if ($this->OS === 'UNIX') {
exec('zip ' . $zipfile . ' ' . $sqlfile, $output, $return);
unlink($sqlfile);
if ($this->options['zipbinary']) {
// use zip binary as it's available
exec('zip ' . $zipfile . ' ' . $sqlfile, $output, $return);
}
else {
// use processwire zip function
$output = wireZipFile($zipfile, $sqlfile);
if (!count($output['errors'])) {
$return = 0;
}
}

unlink($cachePath . 'duplicator.sh');
} else {
exec(str_replace('\\', '/', wire('config')->paths->Duplicator) . 'Bin/7za.exe a ' . $zipfile . ' ' . str_replace('\\', '/', $sqlfile), $output, $return);
unlink($sqlfile);
unlink(str_replace('\\', '/', $cachePath) . 'duplicator.bat');
}

Expand All @@ -113,6 +150,10 @@ public function getZip()
}
}

if (file_exists($sqlfile)) {
unlink($sqlfile);
}

if (file_exists($zipfile)) {
return $zipfile;
}
Expand Down Expand Up @@ -149,6 +190,7 @@ protected function fromNativeTools()

case 'WINDOWS':
return $this->WindowsNative();

default:
// not supported platform
return false;
Expand All @@ -159,27 +201,33 @@ protected function fromNativeTools()

protected function UnixNative()
{
$cachePath = $this->options['cachePath'];
$data = '
# (1) set up all the mysqldump variables
FILE=' . $this->options['backup']['filename'] . '
DBSERVER=127.0.0.1
DATABASE=' . wire('config')->dbName . '
USER=' . wire('config')->dbUser . '
PASS=' . wire('config')->dbPass . '
# (2) in case you run this more than once a day, remove the previous version of the file
unalias rm 2> /dev/null
rm ${FILE} 2> /dev/null
rm ${FILE}.zip 2> /dev/null
# (3) do the mysql database backup (dump)
# for a database server on a separate host:
# mysqldump --opt --protocol=TCP --user=${USER} --password=${PASS} --host=${DBSERVER} ${DATABASE} > ' . $cachePath . '${FILE}
# use this command for a database server on localhost. add other options if need be.
mysqldump --routines --triggers --single-transaction --log-error=mysqldump_error.log --user=${USER} --password=${PASS} --databases ${DATABASE} > ' . $cachePath . '${FILE}
';
if ($this->options['shellScript'] == '') {
$data = $this->setDefaultShellScript();
DUP_Logs::log("- using default shell script");
}
else {
$data = $this->setDefaultShellScript($this->options['shellScript']);
DUP_Logs::log("- using custom shell script");
}

if (!$data) {
throw new WireException("!! Error while running UnixNative Backup\n, err: shell script not found\n\n");
}

// remove carriage return or script will fail on unix
$data = str_replace("\r", "", $data);

$zipExec = shell_exec("which zip");
// check if zip tool is available on env path
if ($zipExec !== null) {
$this->options['zipbinary'] = true;
DUP_Logs::log("- zip binary is available");
} else {
DUP_Logs::log("- zip binary not available, fallback using wireZipFile function");
}

$cachePath = $this->options['cachePath'];

$return = null;
$output = array();
file_put_contents($cachePath . 'duplicator.sh', $data);
Expand All @@ -200,26 +248,49 @@ protected function UnixNative()

// delete `duplicator.sh` script on error
unlink($cachePath . 'duplicator.sh');

$ex = json_encode($output);
throw new WireException("Error while running UnixNative Backup\n, err {$return}: {$ex}\n\n");

$meaning = '';
if ($return == 1) {
$meaning = 'general error in command line';
}
else if ($return == 2) {
$meaning = 'misuse of shell builtins (according to Bash documentation), verify that "mysqldump" or "zip" is available on env path';
}
else if ($return == 126) {
$meaning = 'command invoked cannot execute';
}
else if ($return == 127) {
$meaning = 'error while running the script, maybe the script is not well formatted (carriage return)';
}
else if ($return == 126) {
$meaning = 'command invoked cannot execute';
}
else {
$meaning = 'unknown error';
}

throw new WireException("!! Error while running UnixNative Backup\n, err {$return}: $meaning\n\n");
}

return $cachePath . $this->options['backup']['filename'];
}

protected function WindowsNative()
{
// exit if shell script is empty
if ($this->options['shellScript'] === '') {
return false;
}

$data = $this->setDefaultShellScript($this->options['shellScript']);
if (!$data) {
throw new WireException("!! Error while running UnixNative Backup\n, err: shell script not found\n\n");
}

$return = null;
$output = array();
$cachePath = $this->options['cachePath'];

$data = '@echo off
set MYSQLDATABASE=' . wire('config')->dbName . '
set MYSQLUSER=' . wire('config')->dbUser . '
set MYSQLPASS=' . wire('config')->dbPass . '
"mysqldump.exe" -u%MYSQLUSER% -p%MYSQLPASS% --single-transaction --skip-lock-tables --routines --triggers %MYSQLDATABASE% > ' . $cachePath . $this->options['backup']['filename'];


file_put_contents($cachePath . 'duplicator.bat', $data);

chdir($cachePath);
Expand All @@ -238,4 +309,4 @@ protected function WindowsNative()

return $cachePath . $this->options['backup']['filename'];
}
}
}
32 changes: 29 additions & 3 deletions Classes/DupUtil.php
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,18 @@ public static function isWinOS()
return (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN');
}

// MAC (DARWIN) is threat as UNIX: not tested since M1 Silicon
public static function getOS()
{
$os = strtoupper(substr(PHP_OS, 0, 3));
if ($os === 'WIN') {
return 'WINDOWS';
}
else {
return 'UNIX';
}
}

static public function safePath($path)
{
return str_replace("\\", "/", $path);
Expand Down Expand Up @@ -297,12 +309,26 @@ static function timer($name = 'default', $unset_timer = TRUE)

return $timers;
}
}

static public function getStub($OS = null) {
if ($OS) {
$os = strtolower($OS);
} else {
$os = strtolower(self::getOS());
}
$filename = __DIR__ . '/../scripts/mysqldump.' . $os . '.sh';
// assert script file exists based $OS
if (!file_exists($filename)) {
return false;
}
return $filename;
}

} // end class


class DUP_DataFilter extends \RecursiveFilterIterator
{

protected $excluded;
protected $excludedList = array();

Expand All @@ -327,4 +353,4 @@ public function getChildren()
ini_set('max_execution_time', 300);
return new self($this->getInnerIterator()->getChildren(), $this->excluded);
}
}
} // end class
Loading

0 comments on commit 40cc5ed

Please sign in to comment.