first commit.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Toolbox;
|
||||
|
||||
/**
|
||||
* Autoactualización del instalador global: descarga el phar, verifica su
|
||||
* sha256 contra el publicado y reemplaza el binario de forma atómica.
|
||||
*/
|
||||
final class SelfUpdater
|
||||
{
|
||||
public function __construct(private readonly Console $console)
|
||||
{
|
||||
}
|
||||
|
||||
public function update(string $from, string $target, string $tempDir): void
|
||||
{
|
||||
$source = rtrim($from, '/') . '/duckbrain.phar';
|
||||
$sumUrl = $source . '.sha256';
|
||||
|
||||
$this->console->out(' descargando ' . $source);
|
||||
$data = @file_get_contents($source);
|
||||
if ($data === false) {
|
||||
throw new ToolboxException("No se pudo descargar el instalador: {$source}");
|
||||
}
|
||||
|
||||
$expected = trim((string) @file_get_contents($sumUrl));
|
||||
if ($expected === '') {
|
||||
throw new ToolboxException("No se pudo leer el sha256 publicado: {$sumUrl}");
|
||||
}
|
||||
|
||||
$actual = hash('sha256', $data);
|
||||
if (!hash_equals($expected, $actual)) {
|
||||
throw new ToolboxException('sha256 inválido; actualización abortada sin tocar el binario');
|
||||
}
|
||||
|
||||
if (!is_dir($tempDir)) {
|
||||
mkdir($tempDir, 0777, true);
|
||||
}
|
||||
|
||||
$temp = rtrim($tempDir, '/') . '/duckbrain.phar.' . bin2hex(random_bytes(4));
|
||||
file_put_contents($temp, $data);
|
||||
chmod($temp, 0755);
|
||||
|
||||
if (!rename($temp, $target)) {
|
||||
@unlink($temp);
|
||||
throw new ToolboxException("No se pudo reemplazar el instalador: {$target}");
|
||||
}
|
||||
|
||||
$this->console->ok('instalador actualizado (' . $target . ')');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user