-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
287 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
#!/usr/bin/bash | ||
|
||
ROOTPATH="`dirname $0`" | ||
ROOTPATH="`dirname $ROOTPATH`" | ||
COMPOSER="$ROOTPATH/composer.phar" | ||
|
||
echo " ==> Using composer $COMPOSER" | ||
echo " ==> Will generate $ROOTPATH/db-tools.phar" | ||
|
||
# Make a backup of the composer.json file. | ||
cp "$ROOTPATH/composer.json" "$ROOTPATH/composer.json.dist" | ||
|
||
if [ ! -e "$COMPOSER" ]; then | ||
echo " ==> Download composer in $COMPOSER" | ||
wget --quiet https://getcomposer.org/download/latest-stable/composer.phar -o "$COMPOSER" | ||
fi | ||
|
||
# Prepare composer, install without depdendencies. | ||
echo " ==> Prepare environment" | ||
rm -rf "$ROOTPATH/composer.lock" | ||
rm -rf "$ROOTPATH/vendor" | ||
|
||
# Install PHAR only tooling. | ||
echo " ==> Require compile-only dependencies" | ||
php "$COMPOSER" -n require --no-audit composer/pcre:'^3.1' seld/phar-utils:'^1.2' | ||
php "$COMPOSER" -n -q config autoloader-suffix DbToolsPhar | ||
php "$COMPOSER" -n install --no-dev | ||
php "$COMPOSER" -n config autoloader-suffix --unset | ||
|
||
# Compile PHAR file | ||
echo " ==> Running compilation" | ||
php -d phar.readonly=0 bin/compile.php | ||
chmod +x "$ROOTPATH/db-tools.phar" | ||
|
||
# Clean up environment | ||
echo " ==> Cleaning up environment" | ||
cp "$ROOTPATH/composer.json.dist" "$ROOTPATH/composer.json" | ||
rm -rf "$ROOTPATH/composer.lock" "$ROOTPATH/composer.json.dist" | ||
php "$COMPOSER" -n -q install |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace MakinaCorpus\DbToolsBundle; | ||
|
||
use MakinaCorpus\DbToolsBundle\Bridge\Standalone\PharCompiler; | ||
|
||
/** | ||
* Please run before running this: | ||
* $ composer config autoloader-suffix DbToolsPhar | ||
* $ composer install --no-dev | ||
* $ composer config autoloader-suffix --unset | ||
* $ php -d phar.readonly=0 bin/compile.php | ||
*/ | ||
|
||
(static function (): void { | ||
$autoloadFiles = [ | ||
__DIR__ . '/../vendor/autoload.php', | ||
__DIR__ . '/../../../autoload.php', | ||
]; | ||
|
||
$autoloaderFound = false; | ||
foreach ($autoloadFiles as $autoloadFile) { | ||
if (!\file_exists($autoloadFile)) { | ||
continue; | ||
} | ||
require_once $autoloadFile; | ||
$autoloaderFound = true; | ||
} | ||
|
||
if (!$autoloaderFound) { | ||
if (\extension_loaded('phar') && \Phar::running() !== '') { | ||
\fwrite(STDERR, 'The PHAR was built without dependencies!' . \PHP_EOL); | ||
exit(1); | ||
} | ||
\fwrite(STDERR, 'vendor/autoload.php could not be found. Did you run `composer install`?' . \PHP_EOL); | ||
exit(1); | ||
} | ||
|
||
$cwd = \getcwd(); | ||
\assert(\is_string($cwd)); | ||
\chdir(__DIR__.'/../'); | ||
$ts = \rtrim(\exec('git log -n1 --pretty=%ct HEAD')); | ||
if (!\is_numeric($ts)) { | ||
echo 'Could not detect date using "git log -n1 --pretty=%ct HEAD"'.\PHP_EOL; | ||
exit(1); | ||
} | ||
\chdir($cwd); | ||
|
||
\error_reporting(-1); | ||
\ini_set('display_errors', '1'); | ||
|
||
$compiler = new PharCompiler(); | ||
$compiler->compile(); | ||
exit(1); | ||
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace MakinaCorpus\DbToolsBundle\Bridge\Standalone; | ||
|
||
use Composer\Pcre\Preg; | ||
use Symfony\Component\Finder\Finder; | ||
use Symfony\Component\Process\Process; | ||
use Seld\PharUtils\Timestamps; | ||
|
||
/** | ||
* The Compiler class compiles composer into a phar. | ||
* | ||
* Heavily inspired from composer code, all credits to their authors. | ||
* | ||
* @see https://github.com/composer/composer/blob/main/src/Composer/Compiler.php | ||
* @see https://getcomposer.org/ | ||
*/ | ||
class PharCompiler | ||
{ | ||
private \DateTime $versionDate; | ||
|
||
/** | ||
* Creates the PHAR. | ||
* | ||
* @param ?string $pharFile | ||
* Full target PHAR file name. | ||
*/ | ||
public function compile(?string $pharFile = null): void | ||
{ | ||
$pharFile ??= \dirname(__DIR__, 3) . '/db-tools.phar'; | ||
|
||
if (\file_exists($pharFile)) { | ||
\unlink($pharFile); | ||
} | ||
|
||
$rootDir = \dirname(__DIR__, 3); | ||
|
||
// Next line would fetch the current reference (commit hash or tag). | ||
// $process = new Process(['git', 'log', '--pretty=%H', '-n1', 'HEAD'], $rootDir); | ||
|
||
$process = new Process(['git', 'log', '-n1', '--pretty=%ci', 'HEAD'], $rootDir); | ||
if ($process->run() !== 0) { | ||
throw new \RuntimeException('Can\'t run git log. You must ensure to run compile from composer git repository clone and that git binary is available.'); | ||
} | ||
|
||
$this->versionDate = new \DateTime(\trim($process->getOutput())); | ||
$this->versionDate->setTimezone(new \DateTimeZone('UTC')); | ||
|
||
$phar = new \Phar($pharFile, 0, 'db-tools.phar'); | ||
$phar->setSignatureAlgorithm(\Phar::SHA512); | ||
|
||
$phar->startBuffering(); | ||
|
||
$finderSort = static fn ($a, $b): int => \strcmp(\strtr($a->getRealPath(), '\\', '/'), \strtr($b->getRealPath(), '\\', '/')); | ||
|
||
// Local package sources. | ||
$finder = new Finder(); | ||
$finder->files() | ||
->ignoreVCS(true) | ||
->name('*.php') | ||
->notName('Compiler.php') | ||
->notName('ClassLoader.php') | ||
->notName('InstalledVersions.php') | ||
->in($rootDir.'/src') | ||
->sort($finderSort) | ||
; | ||
foreach ($finder as $file) { | ||
$this->addFile($phar, $file); | ||
} | ||
// Add runtime utilities separately to make sure they retains the docblocks as these will get copied into projects. | ||
$this->addFile($phar, new \SplFileInfo($rootDir . '/vendor/composer/ClassLoader.php'), false); | ||
$this->addFile($phar, new \SplFileInfo($rootDir . '/vendor/composer/InstalledVersions.php'), false); | ||
|
||
// Add vendor files | ||
$finder = new Finder(); | ||
$finder->files() | ||
->ignoreVCS(true) | ||
->notPath('/\/(composer\.(json|lock)|[A-Z]+\.md(?:own)?|\.gitignore|appveyor.yml|phpunit\.xml\.dist|phpstan\.neon\.dist|phpstan-config\.neon|phpstan-baseline\.neon)$/') | ||
->notPath('/(.*\.(md|xml|twig|svg)|Dockerfile|phpbench\.json|yaml-lint|dev\.sh|docker-compose\.(yaml|yml)|run-tests\.sh)/') | ||
->notPath('/bin\/(jsonlint|validate-json|simple-phpunit|phpstan|phpstan\.phar)(\.bat)?$/') | ||
->notPath('justinrainbow/json-schema/demo/') | ||
->notPath('justinrainbow/json-schema/dist/') | ||
->notPath('composer/LICENSE') | ||
->exclude('Tests') | ||
->exclude('tests') | ||
->exclude('docs') | ||
->in($rootDir.'/vendor/') | ||
->sort($finderSort) | ||
; | ||
|
||
$extraFiles = []; | ||
foreach ([ | ||
$rootDir . '/vendor/composer/installed.json', | ||
// CaBundle::getBundledCaBundlePath(), | ||
$rootDir . '/vendor/composer/installed.json', | ||
$rootDir . '/vendor/symfony/console/Resources/bin/hiddeninput.exe', | ||
$rootDir . '/vendor/symfony/console/Resources/completion.bash', | ||
$rootDir . '/vendor/symfony/console/Resources/completion.fish', | ||
$rootDir . '/vendor/symfony/console/Resources/completion.zsh', | ||
$rootDir . '/vendor/composer/installed.json', | ||
] as $file) { | ||
$extraFiles[$file] = \realpath($file); | ||
if (!\file_exists($file)) { | ||
throw new \RuntimeException('Extra file listed is missing from the filesystem: '.$file); | ||
} | ||
} | ||
$unexpectedFiles = []; | ||
|
||
foreach ($finder as $file) { | ||
if (false !== ($index = \array_search($file->getRealPath(), $extraFiles, true))) { | ||
unset($extraFiles[$index]); | ||
} elseif (!Preg::isMatch('{(^LICENSE$|\.php$)}', $file->getFilename())) { | ||
$unexpectedFiles[] = (string) $file; | ||
} | ||
|
||
if (Preg::isMatch('{\.php[\d.]*$}', $file->getFilename())) { | ||
$this->addFile($phar, $file); | ||
} else { | ||
$this->addFile($phar, $file, false); | ||
} | ||
} | ||
|
||
if (\count($extraFiles) > 0) { | ||
throw new \RuntimeException('These files were expected but not added to the phar, they might be excluded or gone from the source package:'.PHP_EOL.var_export($extraFiles, true)); | ||
} | ||
if (\count($unexpectedFiles) > 0) { | ||
throw new \RuntimeException('These files were unexpectedly added to the phar, make sure they are excluded or listed in $extraFiles:'.PHP_EOL.var_export($unexpectedFiles, true)); | ||
} | ||
|
||
// Add binary. | ||
$phar->addFile($rootDir . '/bin/db-tools.php', 'bin/db-tools.php'); | ||
$content = \file_get_contents($rootDir.'/bin/db-tools'); | ||
$content = Preg::replace('{^#!/usr/bin/env php\s*}', '', $content); | ||
$phar->addFromString('bin/db-tools', $content); | ||
|
||
// Stubs | ||
$phar->setStub( | ||
<<<'EOT' | ||
#!/usr/bin/env php | ||
<?php | ||
if (!\class_exists('Phar')) { | ||
echo 'PHP\'s phar extension is missing. DbTools requires it to run. Enable the extension or recompile php without --disable-phar then try again.' . PHP_EOL; | ||
exit(1); | ||
} | ||
Phar::mapPhar('db-tools.phar'); | ||
require 'phar://db-tools.phar/bin/db-tools'; | ||
__HALT_COMPILER(); | ||
EOT, | ||
); | ||
|
||
$phar->stopBuffering(); | ||
|
||
//$this->addFile($phar, new \SplFileInfo($rootDir.'/LICENSE.md'), false); | ||
|
||
unset($phar); | ||
|
||
// re-sign the phar with reproducible timestamp / signature | ||
$util = new Timestamps($pharFile); | ||
$util->updateTimestamps($this->versionDate); | ||
$util->save($pharFile, \Phar::SHA512); | ||
} | ||
|
||
private function getRelativeFilePath(\SplFileInfo $file): string | ||
{ | ||
$rootDir = \dirname(__DIR__, 3); | ||
|
||
$realPath = $file->getRealPath(); | ||
$pathPrefix = $rootDir . DIRECTORY_SEPARATOR; | ||
$pos = \strpos($realPath, $pathPrefix); | ||
$relativePath = ($pos !== false) ? \substr_replace($realPath, '', $pos, \strlen($pathPrefix)) : $realPath; | ||
|
||
return \strtr($relativePath, '\\', '/'); | ||
} | ||
|
||
private function addFile(\Phar $phar, \SplFileInfo $file, bool $strip = true): void | ||
{ | ||
$phar->addFile($this->getRelativeFilePath($file)); | ||
} | ||
} |