Skip to content

Commit

Permalink
Merge pull request #298 from jolicode/fix-update
Browse files Browse the repository at this point in the history
Fix instruction for downloading new castor version as a phar
  • Loading branch information
pyrech committed Feb 27, 2024
2 parents c6c9008 + a73fb60 commit 0f4549c
Show file tree
Hide file tree
Showing 10 changed files with 234 additions and 95 deletions.
4 changes: 2 additions & 2 deletions .github/actions/cache/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ runs:
set -e
# Should be the same command as the one in tools/static/castor.php
cache_dirname_linux_amd64=$(tests/bin/compile-get-cache-key phar-location-is-not-used-in-cache-key --os=linux --arch=x86_64 --php-extensions=mbstring,phar,posix,tokenizer,pcntl)
cache_dirname_linux_amd64=$(tests/bin/compile-get-cache-key phar-location-is-not-used-in-cache-key --os=linux --arch=x86_64 --php-extensions=mbstring,phar,posix,tokenizer,pcntl,curl)
cache_key_linux_amd64=$(basename $cache_dirname_linux_amd64)
echo cache_dirname_linux_amd64=$cache_dirname_linux_amd64 >> $GITHUB_ENV
echo cache_key_linux_amd64=$cache_key_linux_amd64 >> $GITHUB_ENV
# Should be the same command as the one in tools/static/castor.php
cache_dirname_darwin_amd64=$(tests/bin/compile-get-cache-key phar-location-is-not-used-in-cache-key --os=macos --arch=x86_64 --php-extensions=mbstring,phar,posix,tokenizer,pcntl)
cache_dirname_darwin_amd64=$(tests/bin/compile-get-cache-key phar-location-is-not-used-in-cache-key --os=macos --arch=x86_64 --php-extensions=mbstring,phar,posix,tokenizer,pcntl,curl)
cache_key_darwin_amd64=$(basename $cache_dirname_darwin_amd64)
echo cache_dirname_darwin_amd64=$cache_dirname_darwin_amd64 >> $GITHUB_ENV
echo cache_key_darwin_amd64=$cache_key_darwin_amd64 >> $GITHUB_ENV
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,10 @@ jobs:
if: matrix.castor.method == 'phar' || matrix.castor.method == 'static'

- name: Compile Custom Built PHP along Castor phar for Linux
# Should be the same command as the one in tools/static/castor.php
run: bin/castor compile tools/phar/build/castor.linux-amd64.phar --php-extensions=mbstring,phar,posix,tokenizer,pcntl --binary-path=${{ github.workspace }}/${{ matrix.castor.bin }}
run: |
set -e
bin/castor castor:static:linux
mv castor.linux-amd64 ${{ github.workspace }}/${{ matrix.castor.bin }}
if: matrix.castor.method == 'static'

# We use box in a test, so we need to make it available everywhere
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Not released yet

## 0.13.1 (2024-02-27)

* Fix instruction for downloading new castor version as a phar

## 0.13.0 (2024-02-23)

* Add a `compile` command that puts together a customizable PHP binary with a
Expand Down
1 change: 1 addition & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@
<php>
<server name="SYMFONY_PHPUNIT_REQUIRE" value="nikic/php-parser:^4" />
<server name="ENDPOINT" value="http://127.0.0.1:9955" />
<server name="DISABLE_VERSION_CHECK" value="true" />
</php>
</phpunit>
86 changes: 1 addition & 85 deletions src/Console/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,11 @@
use Castor\ListenerDescriptor;
use Castor\PlatformUtil;
use Castor\SectionOutput;
use Castor\Stub\StubsGenerator;
use Castor\SymfonyTaskDescriptor;
use Castor\TaskDescriptor;
use Castor\TaskDescriptorCollection;
use Castor\VerbosityLevel;
use Castor\WaitForHelper;
use JoliCode\PhpOsHelper\OsHelper;
use Monolog\Logger;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Log\LogLevel;
Expand All @@ -37,17 +35,15 @@
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\ErrorHandler\ErrorHandler;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Process\Process;
use Symfony\Component\VarDumper\Cloner\AbstractCloner;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\HttpClient\Exception\ExceptionInterface as HttpExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;

/** @internal */
class Application extends SymfonyApplication
{
public const NAME = 'castor';
public const VERSION = 'v0.13.0';
public const VERSION = 'v0.13.1';

// "Current" objects availables at some point of the lifecycle
private InputInterface $input;
Expand All @@ -61,7 +57,6 @@ public function __construct(
public readonly ContextRegistry $contextRegistry,
public readonly EventDispatcher $eventDispatcher,
public readonly ExpressionLanguage $expressionLanguage,
public readonly StubsGenerator $stubsGenerator,
public readonly Logger $logger,
public readonly Filesystem $fs,
public HttpClientInterface $httpClient,
Expand Down Expand Up @@ -170,11 +165,6 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI
{
$this->command = $command;

if ('_complete' !== $command->getName() && !class_exists(\RepackedApplication::class)) {
$this->stubsGenerator->generateStubsIfNeeded($this->rootDir . '/.castor.stub.php');
$this->displayUpdateWarningIfNeeded(new SymfonyStyle($input, $output));
}

return parent::doRunCommand($command, $input, $output);
}

Expand Down Expand Up @@ -255,78 +245,4 @@ private function configureContext(InputInterface $input, OutputInterface $output

$this->contextRegistry->setCurrentContext($context->withName($input->getOption('context')));
}

private function displayUpdateWarningIfNeeded(SymfonyStyle $symfonyStyle): void
{
$item = $this->cache->getItem('last-update-warning');
if ($item->isHit()) {
return;
}

// We save it right now, even if there are some failures later. We don't
// want to waste bandwidth or CPU usage, nor log too much information.
$item->expiresAfter(60 * 60 * 24);
$item->set(true);
$this->cache->save($item);

$response = $this->httpClient->request('GET', 'https://api.github.com/repos/jolicode/castor/releases/latest', [
'timeout' => 1,
]);

try {
$latestVersion = $response->toArray();
} catch (HttpExceptionInterface) {
$this->logger->info('Failed to fetch latest Castor version from GitHub.');

return;
}

if (version_compare($latestVersion['tag_name'], self::VERSION, '<=')) {
return;
}

$symfonyStyle->block(sprintf('<info>A new Castor version is available</info> (<comment>%s</comment>, currently running <comment>%s</comment>).', $latestVersion['tag_name'], self::VERSION), escape: false);

if ($pharPath = \Phar::running(false)) {
$assets = match (true) {
OsHelper::isWindows() || OsHelper::isWindowsSubsystemForLinux() => array_filter($latestVersion['assets'], fn (array $asset) => str_contains($asset['name'], 'windows')),
OsHelper::isMacOS() => array_filter($latestVersion['assets'], fn (array $asset) => str_contains($asset['name'], 'darwin')),
OsHelper::isUnix() => array_filter($latestVersion['assets'], fn (array $asset) => str_contains($asset['name'], 'linux')),
default => [],
};

if (!$assets) {
$this->logger->info('Failed to detect the correct release url adapted to your system.');

return;
}

$latestReleaseUrl = reset($assets)['browser_download_url'] ?? null;

if (!$latestReleaseUrl) {
$this->logger->info('Failed to fetch latest phar url.');

return;
}

if (OsHelper::isUnix()) {
$symfonyStyle->block('Run the following command to update Castor:');
$symfonyStyle->block(sprintf('<comment>curl "%s" -Lfso castor && chmod u+x castor && mv castor %s</comment>', $latestReleaseUrl, $pharPath), escape: false);
} else {
$symfonyStyle->block(sprintf('Download the latest version at <comment>%s</comment>', $latestReleaseUrl), escape: false);
}

$symfonyStyle->newLine();

return;
}

$process = new Process(['composer', 'global', 'config', 'home', '--quiet']);
$process->run();
$globalComposerPath = trim($process->getOutput());

if ($globalComposerPath && str_contains(__FILE__, $globalComposerPath)) {
$symfonyStyle->block('Run the following command to update Castor: <comment>composer global update jolicode/castor</comment>', escape: false);
}
}
}
12 changes: 11 additions & 1 deletion src/Console/ApplicationFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
use Castor\ExpressionLanguage;
use Castor\Fingerprint\FingerprintHelper;
use Castor\FunctionFinder;
use Castor\Listener\GenerateStubsListener;
use Castor\Listener\UpdateCastorListener;
use Castor\Monolog\Processor\ProcessProcessor;
use Castor\PathHelper;
use Castor\PlatformUtil;
Expand Down Expand Up @@ -48,6 +50,15 @@ public static function create(): SymfonyApplication
$logger = new Logger('castor', [], [new ProcessProcessor()]);
$fs = new Filesystem();
$eventDispatcher = new EventDispatcher(logger: $logger);
$eventDispatcher->addSubscriber(new UpdateCastorListener(
$cache,
$httpClient,
$logger,
));
$eventDispatcher->addSubscriber(new GenerateStubsListener(
new StubsGenerator($logger),
$rootDir,
));

/** @var SymfonyApplication */
// @phpstan-ignore-next-line
Expand All @@ -57,7 +68,6 @@ public static function create(): SymfonyApplication
$contextRegistry,
$eventDispatcher,
new ExpressionLanguage($contextRegistry),
new StubsGenerator($logger),
$logger,
$fs,
$httpClient,
Expand Down
8 changes: 6 additions & 2 deletions src/Console/Command/CompileCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
*/
class CompileCommand extends Command
{
// When something **important** related to the compilation changed, increase
// this version to invalide the cache
private const CACHE_VERSION = '1';

public function __construct(
private readonly HttpClientInterface $httpClient,
private readonly Filesystem $fs
Expand All @@ -37,7 +41,7 @@ protected function configure(): void
->addOption('os', null, InputOption::VALUE_REQUIRED, 'Target OS for PHP compilation', 'linux', ['linux', 'macos'])
->addOption('arch', null, InputOption::VALUE_REQUIRED, 'Target architecture for PHP compilation', 'x86_64', ['x86_64', 'aarch64'])
->addOption('php-version', null, InputOption::VALUE_REQUIRED, 'PHP version in major.minor format', '8.3')
->addOption('php-extensions', null, InputOption::VALUE_REQUIRED, 'PHP extensions required, in a comma-separated format. Defaults are the minimum required to run a basic "Hello World" task in Castor.', 'mbstring,phar,posix,tokenizer')
->addOption('php-extensions', null, InputOption::VALUE_REQUIRED, 'PHP extensions required, in a comma-separated format. Defaults are the minimum required to run a basic "Hello World" task in Castor.', 'mbstring,phar,posix,tokenizer,curl')
->addOption('php-rebuild', null, InputOption::VALUE_NONE, 'Ignore cache and force PHP build compilation.')
->setHidden(true)
;
Expand Down Expand Up @@ -253,7 +257,7 @@ private function generatePHPBuildCacheKey(InputInterface $input): string
sort($phpExtensions);
hash_update($c, implode(',', $phpExtensions));

hash_update_file($c, __FILE__);
hash_update($c, self::CACHE_VERSION);

return hash_final($c);
}
Expand Down
43 changes: 43 additions & 0 deletions src/Listener/GenerateStubsListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace Castor\Listener;

use Castor\Stub\StubsGenerator;
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class GenerateStubsListener implements EventSubscriberInterface
{
public function __construct(
private readonly StubsGenerator $stubsGenerator,
private readonly string $rootDir,
) {
}

public function generateStubs(ConsoleCommandEvent $event): void
{
if (class_exists(\RepackedApplication::class)) {
return;
}

$command = $event->getCommand();
if (!$command) {
return;
}
if ('_complete' === $command->getName()) {
return;
}

$this->stubsGenerator->generateStubsIfNeeded($this->rootDir . '/.castor.stub.php');
}

public static function getSubscribedEvents(): array
{
return [
// Must be before the command is executed, because we have to check
// for many command options
ConsoleEvents::COMMAND => 'generateStubs',
];
}
}

0 comments on commit 0f4549c

Please sign in to comment.