eveseat/installer

View on GitHub
src/Utils/Composer.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php

/*
 * This file is part of SeAT
 *
 * Copyright (C) 2015 to 2021 Leon Jacobs
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

namespace Seat\Installer\Utils;

use GuzzleHttp\Client;
use Seat\Installer\Exceptions\ComposerInstallException;
use Seat\Installer\Traits\FindsExecutables;
use Seat\Installer\Utils\Abstracts\AbstractUtil;
use Symfony\Component\Filesystem\Filesystem;

/**
 * Class Composer.
 * @package Seat\Installer\Utils
 */
class Composer extends AbstractUtil
{
    use FindsExecutables;

    /**
     * @var string
     */
    protected $composer_sig = 'https://composer.github.io/installer.sig';

    /**
     * @var string
     */
    protected $composer_url = 'https://getcomposer.org/installer';

    /**
     * @var string
     */
    protected $temp_filestore = '/tmp/composer';

    /**
     * @var string
     */
    protected $executable_path = '/usr/local/bin';

    /**
     * Install composer.
     */
    public function install()
    {

        // Download composer
        $this->io->text('Downloading Composer from ' . $this->composer_url);
        $this->downloadComposer();

        // Get the signature that we should expect.
        $this->io->text('Downloading and verifying signatures');
        $signature = $this->getSignature();
        // Hash the downloaded file to match against.
        $downloaded_signature = hash_file('SHA384', $this->temp_filestore);

        // Check the signatures if the downloaded file and the sig file.
        if ($signature != $downloaded_signature)
            throw new ComposerInstallException('Signature mismatch');
        $this->io->text('Running Composer Installer');
        $this->runInstaller();

        $this->io->text('Checking that PATH is configured correctly');
        $this->checkPath();

        $this->io->text('Checking if composer can now be found.');
        if (! $this->hasComposer())
            throw new ComposerInstallException('Composer could not be found after installation');
        $this->io->success('Composer Installation Complete');

    }

    /**
     * Download Composer Installer.
     */
    private function downloadComposer()
    {

        $client = new Client();
        $client->request('get', $this->composer_url, [
            'sink' => $this->temp_filestore,
        ]);

    }

    /**
     * Get the latest signature for verification.
     */
    private function getSignature(): string
    {

        // Perform the download
        $client = new Client();
        $response = $client->request('get', $this->composer_sig);

        return trim($response->getBody()->getContents());

    }

    /**
     * Run the Composer Installer.
     *
     * @throws \Seat\Installer\Exceptions\ComposerInstallException
     */
    private function runInstaller()
    {

        // Prepare the installation command.
        $command = $this->findExecutable('php') . ' ' . $this->temp_filestore .
            ' --install-dir=' . $this->executable_path . ' --filename=composer';

        // Run the install.
        $success = $this->runCommandWithOutput($command, 'Composer Installation');

        // Make sure composer installed fine.
        if (! $success)
            throw new ComposerInstallException('Composer installation failed.');
        // Cleanup the tempfile
        $fs = new Filesystem();
        $fs->remove($this->temp_filestore);

    }

    /**
     * Check if the composer installation directory is in
     * the users PATH.
     */
    private function checkPath()
    {

        $path = getenv('PATH');

        if (! strpos($path, $this->executable_path))
            $this->io->warning('Installation path ' . $this->executable_path . ' is not ' .
                'in the PATH environment variable. This may cause tools to fail when ' .
                'they need to use composer.');

        $this->io->text('PATH containts the installation path of ' . $this->executable_path);
    }

    /**
     * Check if composer binary can be found.
     *
     * @return bool
     */
    public function hasComposer(): bool
    {

        return $this->hasExecutable('composer');
    }

    /**
     * OsUpdates a Composer installation.
     *
     * @throws \Seat\Installer\Exceptions\ComposerInstallException
     */
    public function update()
    {

        // Ensure that we have Composer
        if (! $this->hasComposer())
            throw new ComposerInstallException('Cant find composer to update. Maybe install it first?');
        // Prep and run the update
        $command = $this->findExecutable('composer') . ' self-update --no-interaction --no-ansi';
        $success = $this->runCommandWithOutput($command, '');

        if (! $success)
            throw new ComposerInstallException('Failed to update Composer');
        $this->io->success('Composer Update Complete');
    }

    /**
     * Update composer installed packages in a specific path.
     *
     * @param string $path
     * @param bool   $include_dev
     *
     * @throws \Seat\Installer\Exceptions\ComposerInstallException
     */
    public function updatePackages(string $path, $include_dev = false)
    {

        // Ensure that we have Composer
        if (! $this->hasComposer())
            throw new ComposerInstallException('Cant find composer to update. Maybe install it first?');
        // Prep and run the update
        $command = $this->findExecutable('composer') . ' update --no-interaction --no-ansi ' .
            ($include_dev ? '' : '--no-dev ') .
            '--no-progress -d ' . $path;
        $success = $this->runCommandWithOutput($command, '');

        if (! $success)
            throw new ComposerInstallException('Failed to update Composer');
    }
}