bootstrap-dev
#!/usr/bin/env php
<?php
class BootstrapError extends \Exception { }
trait SingletonSupport
{
private static $_instance = null;
public static function instance(...$args)
{
if(!is_object(self::$_instance)) {
self::$_instance = new self(...$args);
}
return self::$_instance;
}
}
abstract class AbstractInstaller {
protected $install_dir;
protected $filename;
protected $url;
protected $sig_url;
protected $tmpdir;
protected $tmpfile;
protected $remove_tmpdir;
static protected $currentSystem;
static protected $currentMachine;
protected function __construct( $install_dir = null,
$filename = null,
$url = null,
$sig_url = null,
$tmpdir = null)
{
if(!isset($install_dir)) { $install_dir = $this->defaultInstallDir(); }
if(!isset($filename)) { $filename = $this->defaultFileName(); }
if(!isset($url)) { $url = $this->defaultUrl(); }
if(!isset($sig_url)) { $sig_url = $this->defaultSigUrl(); }
if(!isset($tmpdir)) { $tmpdir = $this->defaultTmpDir(); }
$this->_init($install_dir, $filename, $url, $sig_url, $tmpdir);
}
abstract protected function defaultFileName();
abstract protected function defaultUrl();
abstract protected function defaultSigUrl();
abstract protected function tmpFileNamePrefix();
abstract protected function runInstallCmd();
protected function defaultInstallDir() { return '.'; }
protected function defaultTmpDir() { return 'tmp'; }
static public function system()
{
if(!isset(self::$system)) { self::$currentSystem = php_uname('s'); }
return self::$currentSystem;
}
static public function machine()
{
if(!isset(self::$machine)) { self::$currentMachine = php_uname('m'); }
return self::$currentMachine;
}
final private function __clone() { }
final private function __wakeup() { }
public function __destruct()
{
$this->_cleanup();
}
private function _init($install_dir, $filename, $url, $sig_url, $tmpdir)
{
$this->install_dir = $install_dir;
$this->filename = $filename;
$this->url = $url;
$this->sig_url = $sig_url;
$this->tmpdir = $tmpdir;
$basename = $this->tmpFileNamePrefix() . uniqid(rand(), true) . '.php';
$this->tmpfile = implode(DIRECTORY_SEPARATOR, array($tmpdir, $basename));
$this->remove_tmpdir = false;
}
private function _prepare()
{
if(!file_exists($this->tmpdir)) {
if(!mkdir($this->tmpdir)) {
$msg = "Failed to create '" . $this->tmpdir . "': " . (error_get_last()['message']);
throw new BootstrapError($msg);
}
$this->remove_tmpdir = true;
}
if(file_exists($this->tmpfile)) {
unlink($this->tmpfile);
}
}
private function _cleanup()
{
if(file_exists($this->tmpfile)) {
unlink($this->tmpfile);
}
if($this->remove_tmpdir && file_exists($this->tmpdir)) {
rmdir($this->tmpdir);
$this->remove_tmpdir = false;
}
}
private function _downloadInstaller()
{
if(!copy($this->url, $this->tmpfile)) {
$msg = "Failed to download '" . $this->url . "' => '" . $this->tmpfile . "': " . (error_get_last()['message']);
throw new BootstrapError($msg);
}
}
private function _getExpectedSignature()
{
$sig = file_get_contents($this->sig_url);
if($sig === false) {
$msg = "Failed to download installer signature: " . (error_get_last()['message']);
throw new BootstrapError($msg);
}
return trim($sig);
}
private function _getActualSignature()
{
$sig = hash_file('SHA384', $this->tmpfile);
if($sig === false) {
$msg = "Failed to compute installer signatur : " . (error_get_last()['message']);
throw new BootstrapError($msg);
}
return $sig;
}
private function _checkSignature($expected, $actual)
{
if ($actual !== $expected) {
$msg = 'Installer corrupt (signatures do not match)';
throw new BootstrapError($msg);
}
}
public function install()
{
try {
$this->_prepare();
$this->_downloadInstaller();
if(isset($this->sig_url)) {
$expected = $this->_getExpectedSignature();
$actual = $this->_getActualSignature();
$this->_checkSignature($expected, $actual);
}
$this->runInstallCmd();
} finally {
$this->_cleanup();
}
}
}
class ComposerInstaller extends AbstractInstaller
{
use SingletonSupport;
protected function defaultUrl() { return 'https://getcomposer.org/installer'; }
protected function defaultSigUrl() { return 'https://composer.github.io/installer.sig'; }
protected function defaultFileName() { return 'composer'; }
protected function tmpFileNamePrefix() { return 'composer-setup-'; }
protected function runInstallCmd()
{
$cmd = "php " . $this->tmpfile .
" --quiet --install-dir=" . $this->install_dir .
" --filename=" . $this->filename;
system($cmd);
}
}
class PhpDocInstaller extends AbstractInstaller
{
use SingletonSupport;
protected function defaultUrl() { return 'https://github.com/phpDocumentor/phpDocumentor2/releases/latest/download/phpDocumentor.phar'; }
protected function defaultSigUrl() { return null; }
protected function defaultFileName() { return 'phpdoc'; }
protected function tmpFileNamePrefix() { return 'phpdoc-'; }
protected function runInstallCmd()
{
$destfile = implode(DIRECTORY_SEPARATOR, array($this->install_dir, $this->filename));
if(!copy($this->tmpfile, $destfile)) {
$msg = "Failed to copy file '". $this->tmpfile ."' => '". $destfile ."': ". (error_get_last()['message']);
throw new BootstrapError($msg);
}
if(!chmod($destfile, 0755)) {
$msg = "Failed to chmod on '". $destfile ."': ". (error_get_last()['message']);
throw new BootstrapError($msg);
}
}
}
class DockerComposerInstaller extends AbstractInstaller
{
use SingletonSupport;
protected function defaultUrl() {
$release = '1.23.0';
$fname = 'docker-compose-' . self::system(). '-' . self::machine();
if('win' === strtolower(substr(self::system(),0,3))) {
$fname = $fname . '.exe';
}
return "https://github.com/docker/compose/releases/download/$release/$fname";
}
protected function defaultSigUrl() { return null; }
protected function defaultFileName() {
if('win' === strtolower(substr(self::system(),0,3))) {
return 'docker-compose.exe';
} else {
return 'docker-compose';
}
}
protected function tmpFileNamePrefix() { return 'docker-compose-'; }
protected function runInstallCmd()
{
$destfile = implode(DIRECTORY_SEPARATOR, array($this->install_dir, $this->filename));
if(!copy($this->tmpfile, $destfile)) {
$msg = "Failed to copy file '". $this->tmpfile ."' => '". $destfile ."': ". (error_get_last()['message']);
throw new BootstrapError($msg);
}
if(!chmod($destfile, 0755)) {
$msg = "Failed to chmod on '". $destfile ."': ". (error_get_last()['message']);
throw new BootstrapError($msg);
}
}
}
function updateDotEnvFile()
{
$dotenv = __DIR__.'/.env';
if(file_exists($dotenv)) {
fprintf(STDERR, "info: %s\n", "file $dotenv already exists, skipping creation");
return false;
}
$dist = __DIR__.'/.env.dist';
$uid = (string)getmyuid();
$gid = (string)getmygid();
$content = file_get_contents($dist);
$content = preg_replace('/@UID@/', $uid, $content);
$content = preg_replace('/@GID@/', $gid, $content);
file_put_contents($dotenv, $content);
return true;
}
try {
$opts = getopt('', ['no-install']);
if (!isset($opts['no-install'])) {
ComposerInstaller::instance()->install();
PhpDocInstaller::instance()->install();
DockerComposerInstaller::instance()->install();
}
updateDotEnvFile();
} catch(BootstrapError $e) {
fprintf(STDERR, "error: %s\n", $e->getMessage());
exit(1);
}
?>