All checks were successful
		
		
	
	ci/woodpecker/push/woodpecker Pipeline was successful
				
			
		
			
				
	
	
		
			149 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			149 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
 | 
						|
namespace App\Rclone;
 | 
						|
 | 
						|
use Psr\Log\LoggerAwareTrait;
 | 
						|
use Psr\Log\NullLogger;
 | 
						|
use Psr\Log\LoggerInterface;
 | 
						|
use Symfony\Component\Process\Process;
 | 
						|
use Symfony\Component\Process\Exception\ProcessFailedException;
 | 
						|
use Exception;
 | 
						|
 | 
						|
/**
 | 
						|
 * Wrapper for the rclone command line utility
 | 
						|
 *
 | 
						|
 * Installation of rclone is required.
 | 
						|
 * Configuration of the mounts must be done before use.
 | 
						|
 * Tested using rclone v1.53.3-DEV
 | 
						|
 */
 | 
						|
class Rclone
 | 
						|
{
 | 
						|
    use LoggerAwareTrait;
 | 
						|
 | 
						|
    protected string $rclonePath;
 | 
						|
    /**
 | 
						|
     * Global options
 | 
						|
     *
 | 
						|
     * @var array<string>
 | 
						|
     */
 | 
						|
    protected array $globalOptions = [];
 | 
						|
 | 
						|
    protected string $version = "";
 | 
						|
 | 
						|
    /**
 | 
						|
     * Create a new instance.
 | 
						|
     *
 | 
						|
     * Default it looks for "rclone" on the path.
 | 
						|
     * But the path can be configured to be absolute.
 | 
						|
     *
 | 
						|
     * @param string $rclonePath Relative or absolute path
 | 
						|
     */
 | 
						|
    public function __construct(string $rclonePath = "rclone")
 | 
						|
    {
 | 
						|
        $this->rclonePath = $rclonePath;
 | 
						|
        $this->setLogger(new NullLogger());
 | 
						|
 | 
						|
        try {
 | 
						|
            $version = $this->exec('--version');
 | 
						|
            $this->version = explode("\n", $version)[0];
 | 
						|
        } catch (ProcessFailedException $e) {
 | 
						|
            throw new Exception("Check installation of rclone");
 | 
						|
        }
 | 
						|
 | 
						|
        if (!\str_contains($this->version, 'rclone')) {
 | 
						|
            throw new Exception("Rclone not recognized");
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Get the rclone version.
 | 
						|
     *
 | 
						|
     * @return string Version string
 | 
						|
     */
 | 
						|
    public function getVersion(): string
 | 
						|
    {
 | 
						|
        return $this->version;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Calculate the size of a mount/path.
 | 
						|
     *
 | 
						|
     * @param string $path mount/path.
 | 
						|
     *
 | 
						|
     * @return int Size in bytes
 | 
						|
     */
 | 
						|
    public function getSize(string $path): int
 | 
						|
    {
 | 
						|
        $output = $this->exec('size', ['--json', $path]);
 | 
						|
        return (int)json_decode($output)->bytes;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Copy from source to destination.
 | 
						|
     *
 | 
						|
     * @param $src                          Source mount and path
 | 
						|
     * @param $dest                         Destination mount and path
 | 
						|
     * @param $additionalOptions strings[]  Bandwidth limit provided as string
 | 
						|
     *
 | 
						|
     * @return string Stdout from command
 | 
						|
     */
 | 
						|
    public function copy(string $src, string $dest, array $additionalOptions = array()): string
 | 
						|
    {
 | 
						|
        $options = array();
 | 
						|
 | 
						|
        $options[] = $src;
 | 
						|
        $options[] = $dest;
 | 
						|
        foreach ($additionalOptions as $key => $value) {
 | 
						|
            if (strlen($key) == 1) {
 | 
						|
                $options[] = '-' . $key;
 | 
						|
                $options[] = $value;
 | 
						|
            } elseif (strlen($key) > 2) {
 | 
						|
                $options[] = '--' . $key;
 | 
						|
                $options[] = $value;
 | 
						|
            } else {
 | 
						|
                $options[] = $value;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return $this->exec('copy', $options);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Execute a command on the rclone binary.
 | 
						|
     *
 | 
						|
     * @param string        $command Top level Rclone command
 | 
						|
     * @param array<String> $options Array of additional options
 | 
						|
     *
 | 
						|
     * @return string stdout data.
 | 
						|
     */
 | 
						|
    protected function exec(string $command, array $options = array()): string
 | 
						|
    {
 | 
						|
        $process = new Process(
 | 
						|
            array_merge(
 | 
						|
                [$this->rclonePath],
 | 
						|
                $this->globalOptions,
 | 
						|
                [$command],
 | 
						|
                $options
 | 
						|
            )
 | 
						|
        );
 | 
						|
        if ($this->logger instanceof LoggerInterface) {
 | 
						|
            $this->logger->info("Execute command", [$process->getCommandLine()]);
 | 
						|
        }
 | 
						|
 | 
						|
        $process->setTimeout(4 * 3600);
 | 
						|
        $process->run();
 | 
						|
 | 
						|
        // executes after the command finishes
 | 
						|
        if (!$process->isSuccessful()) {
 | 
						|
            if ($this->logger instanceof LoggerInterface) {
 | 
						|
                $this->logger->error("Failed execution");
 | 
						|
            }
 | 
						|
            throw new ProcessFailedException($process);
 | 
						|
        }
 | 
						|
        if ($this->logger instanceof LoggerInterface) {
 | 
						|
            $this->logger->info("Return code", [$process->getExitCode()]);
 | 
						|
        }
 | 
						|
        return $process->getOutput();
 | 
						|
    }
 | 
						|
}
 |