Skip to content

Commit

Permalink
Merge pull request #93 from hydephp/release/v0.7.18
Browse files Browse the repository at this point in the history
HydeCLI v0.7.18
  • Loading branch information
caendesilva authored Apr 19, 2024
2 parents dafe203 + 26bdd6a commit 9ea4345
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 121 deletions.
2 changes: 1 addition & 1 deletion app/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

class Application extends \Hyde\Foundation\Application
{
final public const APP_VERSION = '0.7.17';
final public const APP_VERSION = '0.7.18';

public function getCachedPackagesPath(): string
{
Expand Down
144 changes: 28 additions & 116 deletions app/Commands/SelfUpdateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,22 @@
use RuntimeException;
use Illuminate\Support\Str;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Process;
use App\Commands\Internal\ReportsSelfUpdateCommandIssues;

use function trim;
use function exec;
use function time;
use function file;
use function fopen;
use function chmod;
use function umask;
use function touch;
use function fclose;
use function rename;
use function unlink;
use function usleep;
use function filled;
use function explode;
use function ini_set;
use function sprintf;
use function implode;
use function tempnam;
use function passthru;
use function realpath;
use function in_array;
use function array_map;
use function curl_init;
Expand All @@ -41,14 +35,11 @@
use function json_decode;
use function is_writable;
use function curl_setopt;
use function str_replace;
use function array_combine;
use function str_ends_with;
use function clearstatcache;
use function sys_get_temp_dir;
use function extension_loaded;
use function file_get_contents;
use function file_put_contents;
use function get_included_files;

/**
Expand All @@ -75,6 +66,8 @@ class SelfUpdateCommand extends Command
protected const STRATEGY_DIRECT = 'direct';
protected const STRATEGY_COMPOSER = 'composer';

protected const COMPOSER_COMMAND = 'composer global require hyde/cli';

/** @var array<string, string|array<string>> The latest release information from the GitHub API */
protected array $release;

Expand Down Expand Up @@ -129,7 +122,7 @@ public function handle(): int

$this->info('The application has been updated successfully.');

// Verify the application version (// Fixme: This shows the old version when using Composer to update)
// Verify the application version
passthru('hyde --version --ansi');

// Now we can exit the application, we do this manually to avoid issues when Laravel tries to clean up the application
Expand Down Expand Up @@ -355,42 +348,39 @@ protected function updateViaComposer(): void
{
$this->output->writeln('Updating via Composer...');

$command = 'composer global require hyde/cli';

/** @experimental Support for elevating Windows privileges */
if (PHP_OS_FAMILY === 'Windows') {
$path = $this->findApplicationPath();

// Check if this is the expected path, so we don't try anything crazy
if (str_ends_with($path, '\AppData\Roaming\Composer\vendor\bin\hyde')) {
// Early check to see if our process has the required privileges
if (! is_writable($path) || exec('where cscript') === '') {
$this->error('The application path is not writable. Please rerun the command with elevated privileges.');
exit(126);
}

// The called Composer process probably will not have the required privileges, so we need to elevate them
if ($this->confirm('The application path may require elevated privileges to update. Do you want to provide administrator permissions, or try updating without?', true)) {
// Attempt to release the file path
clearstatcache(true, $path);
unset($path);

$this->runComposerInElevatedPrompt();

return;
}
}
$exitCode = $this->runComposerCommandOnWindows();
} else {
// Invoke the Composer command to update the application
passthru(self::COMPOSER_COMMAND, $exitCode);
}

// Invoke the Composer command to update the application
passthru($command, $exitCode);

if ($exitCode !== 0) {
$this->error('The Composer command failed with exit code '.$exitCode);
exit($exitCode);
}
}

protected function runComposerCommandOnWindows(): int
{
// Running the Composer process on Windows may require extra privileges,
// so in order to improve the UX, we run a more low level interaction
// than is needed on Unix systems, so we can read the output since
// Composer sends almost all output to STDERR instead of STDOUT
// which is not captured by `passthru()` or `shell_exec()`

$process = Process::timeout(30);

$output = [];

$result = $process->run(self::COMPOSER_COMMAND, function (string $type, string $buffer) use (&$output): void {
$this->output->writeln('<fg=gray> > '.trim($buffer).'</>');
$output[] = $buffer;
});

return $result->exitCode();
}

protected function debug(string $message): void
{
if ($this->output->isVerbose()) {
Expand All @@ -413,82 +403,4 @@ protected function printNewlineIfVerbose(): void
{
$this->debug('');
}

/** @experimental This is highly experimental and may be unstable. */
protected function runComposerInElevatedPrompt(): void
{
// Invokes a UAC prompt to run composer as an admin
// Uses a .vbs script to elevate and run the cmd.exe composer command.
// Based on https://github.com/composer/composer/blob/main/src/Composer/Command/SelfUpdateCommand.php#L596

// In order to get the output, we need a proxy batch file to redirect the output to a file that we can use as a substitute stream

$path = tempnam(sys_get_temp_dir(), 'hyde-update');
$outputStream = "$path.log";

// Create the output stream file
touch($outputStream);

$outputStream = realpath($outputStream);

// Set up a batch script so we can redirect the output to our stream file
$batch = str_replace('{{ outputStream }}', $outputStream, file_get_contents(__DIR__.'/../bin/composer-update-proxy.bat'));

$updateScript = "$path.bat";
file_put_contents($updateScript, $batch);
$updateScript = realpath($updateScript);

$vbs = str_replace('{{ updateScript }}', $updateScript, file_get_contents(__DIR__.'/../bin/composer-update-proxy.vbs'));

$vbsScript = "$path.vbs";
file_put_contents($vbsScript, $vbs);

// Run the script
exec("cscript //nologo $vbsScript");

// ShellExecute is async, so we read the file to stream the output to the console to get logs and to know when it is done

$this->streamBatchOutput($outputStream);

@unlink($vbsScript);
@unlink($updateScript);
@unlink($outputStream);
}

/** @experimental This is highly experimental and may be unstable. */
protected function streamBatchOutput(string $outputStream): void
{
$timeout = 30;
$start = time();
$writtenLines = [];

while (true) {
// Stream the log file until we see the end of the output
$log = file($outputStream);

foreach ($log as $line) {
if (trim($line) === '--END--') {
return;
}

if (! in_array($line, $writtenLines, true)) {
$this->output->writeln('<fg=gray> > '.trim($line).'</>');
$writtenLines[] = $line; // Prevent duplicate lines
}
}

// If we have run for 5 seconds and have no output at all, something is wrong (probably we did not get UAC permission)
if (empty($log) && (time() - $start > 5)) {
$timeout = 0;
}

if (time() - $start > $timeout) {
$this->error('The Composer command timed out. Please try again.');
exit(1);
}

// Sleep for 250ms
usleep((int) (0.250 * 1_000_000));
}
}
}
2 changes: 0 additions & 2 deletions app/bin/composer-update-proxy.bat

This file was deleted.

2 changes: 0 additions & 2 deletions app/bin/request-composer-user-access-control.vbs

This file was deleted.

Binary file modified builds/hyde
Binary file not shown.

0 comments on commit 9ea4345

Please sign in to comment.