Skip to content

Commit

Permalink
Merge pull request #23 from paragonie/release-bundlers
Browse files Browse the repository at this point in the history
Release Bundlers
  • Loading branch information
paragonie-security authored Sep 9, 2021
2 parents 8b5ac38 + 0fa1d0f commit 78a81b8
Show file tree
Hide file tree
Showing 15 changed files with 543 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,4 @@ jobs:
composer-options: --ignore-platform-reqs

- name: PHPUnit tests
run: vendor/bin/phpunit
run: php -d phar.readonly=0 vendor/bin/phpunit
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@
"php": "^5.6|^7|^8",
"ext-json": "*",
"ext-pdo": "*",
"ext-zip": "*",
"paragonie/certainty": "^2.8",
"paragonie/easydb": "^1|^2",
"paragonie/sodium_compat": "^1.17"
"paragonie/sodium_compat": "^1.17",
"pear/archive_tar": "^1.4"
},
"require-dev": {
"guzzlehttp/guzzle": "^6|^7",
Expand Down
1 change: 1 addition & 0 deletions lib/Client/GossamerClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
namespace ParagonIE\Gossamer\Client;

use ParagonIE\Gossamer\Release\Common;
use ParagonIE\Gossamer\TypeHelperTrait;

/**
* Class GossamerClient
Expand Down
2 changes: 1 addition & 1 deletion lib/Client/TrustMode/FederatedTrust.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
namespace ParagonIE\Gossamer\Client\TrustMode;

use ParagonIE\Gossamer\Client\TrustModeInterface;
use ParagonIE\Gossamer\Client\TypeHelperTrait;
use ParagonIE\Gossamer\TypeHelperTrait;
use ParagonIE\Gossamer\Client\UpdateFile;
use ParagonIE\Gossamer\GossamerException;
use ParagonIE\Gossamer\Interfaces\HttpInterface;
Expand Down
17 changes: 17 additions & 0 deletions lib/Interfaces/ReleaseBundlerInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php
namespace ParagonIE\Gossamer\Interfaces;

interface ReleaseBundlerInterface
{
/**
* @param string $directory
* @return self
*/
public function setWorkDirectory($directory);

/**
* @param string $outputFile
* @return bool
*/
public function bundle($outputFile);
}
29 changes: 29 additions & 0 deletions lib/Release/Bundler/AbstractBundler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace ParagonIE\Gossamer\Release\Bundler;

use Exception;
use ParagonIE\Gossamer\Interfaces\ReleaseBundlerInterface;
use ParagonIE\Gossamer\TypeHelperTrait;

abstract class AbstractBundler implements ReleaseBundlerInterface
{
use TypeHelperTrait;


/** @var string $directory */
protected $directory = '';

/**
* @param string $directory
* @return self
* @throws Exception
*/
public function setWorkDirectory($directory)
{
$this->assert(is_string($directory), "Argument 1 must be a string");
$this->assert(is_dir($directory), "Given path is not a directory: {$directory}");
$this->directory = $directory;
return $this;
}
}
79 changes: 79 additions & 0 deletions lib/Release/Bundler/GitDiffBundler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php
namespace ParagonIE\Gossamer\Release\Bundler;

use Exception;
use ParagonIE\Gossamer\GossamerException;

class GitDiffBundler extends AbstractBundler
{
const IDENTIFIER_REGEX = '#[a-zA-Z0-9\-_\.]+#';

/** @var string $previousIdentifier */
protected $previousIdentifier = '';

/**
* @param string $commitOrTag
* @return self
* @throws Exception
*/
public function setPreviousIdentifier($commitOrTag)
{
$this->assert(is_string($commitOrTag), "Argument 1 must be a string");
$this->assert(
preg_match(self::IDENTIFIER_REGEX, $commitOrTag) > 0,
"Invalid commit or tag identifier"
);
$this->previousIdentifier = $commitOrTag;
return $this;
}

/**
* @param string $current
* @return string
* @throws Exception
*/
public function getGitDiff($current = 'HEAD')
{
$this->assert(is_string($current));
$this->assert(
preg_match(self::IDENTIFIER_REGEX, $current) > 0,
"Invalid commit or tag identifier"
);
$this->assert(!empty($this->previousIdentifier));
$this->assert(
preg_match(self::IDENTIFIER_REGEX, $this->previousIdentifier) > 0,
"Invalid commit or tag identifier"
);
$workingDir = \getcwd();

// @todo Support more techniques
if (is_callable('shell_exec')) {
\chdir($this->directory);
/**
* We need to use shell_exec() to get the output of git diff.
* @psalm-suppress ForbiddenCode
*/
$result = (string) @shell_exec(
"git diff {$this->previousIdentifier}..{$current} -G."
);
\chdir($workingDir);
return $result;
}

// Failure case: Throw.
throw new GossamerException("No supported git diff method found");
}

/**
* @param string $outputFile
* @param string $current
* @return bool
* @throws Exception
*/
public function bundle($outputFile, $current = 'HEAD')
{
$diff = $this->getGitDiff($current);
$this->assert(is_string($diff), "Return type of getGitDiff() must be string");
return is_int(file_put_contents($outputFile, $diff));
}
}
53 changes: 53 additions & 0 deletions lib/Release/Bundler/PharBundler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php
namespace ParagonIE\Gossamer\Release\Bundler;

use ParagonIE\Gossamer\GossamerException;
use Phar;

class PharBundler extends AbstractBundler
{
/** @var string $defaultStubFilename */
protected $defaultStubFilename = 'default-stub.php';

/**
* @param string $filename
* @return self
*/
public function setDefaultStubFilename($filename)
{
$this->assert(is_string($filename), "Argument 1 must be a string");
$this->defaultStubFilename = $filename;
return $this;
}

/**
* @return void
* @throws GossamerException
*/
public function throwIfPharReadonly()
{
$readOnly = (bool) ini_get('phar.readonly');
if ($readOnly) {
throw new GossamerException(
"Cannot work with Phars without setting readonly to false"
);
}
}

public function bundle($outputFile)
{
$this->throwIfPharReadonly();
$workingDir = \getcwd();
\chdir($this->directory);

$phar = new Phar($outputFile);
$phar->startBuffering();
$defaultStub = $phar->createDefaultStub($this->defaultStubFilename);
$phar->buildFromDirectory($this->directory);
$phar->setStub('#!/usr/bin/env php' . "\n" . $defaultStub);
$phar->stopBuffering();

\chdir($workingDir);
return is_file($outputFile);
}
}
73 changes: 73 additions & 0 deletions lib/Release/Bundler/TarBundler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php
namespace ParagonIE\Gossamer\Release\Bundler;

use Archive_Tar;

class TarBundler extends AbstractBundler
{
/** @var ?string $compression */
protected $compression = null;

/**
* @return ?string
*/
public function getCompression()
{
return $this->compression;
}

/**
* @param ?string $compress
* @return self
* @throws \Exception
*/
public function setCompression($compress)
{
if (is_null($compress)) {
$this->compression = null;
return $this;
}
$this->assert(
is_string($compress),
"Argument 1 must be a string or null"
);
$this->assert(
in_array($compress, ['gz', 'bz2', 'lzma2']),
"Invalid compression method: " . $compress
);
$this->compression = $compress;
return $this;
}

/**
* @param string $parent
* @return array
*/
public function listFilesToTar($parent)
{
/** @var string $file */
$paths = [];
foreach (glob($parent . '/*') as $file) {
if (is_dir($file)) {
$paths = array_merge($paths, $this->listFilesToTar($file));
} else {
$paths[] = str_replace($this->directory . '/', '', $file);
}
}
return $paths;
}

public function bundle($outputFile)
{
$workingDir = \getcwd();
\chdir($this->directory);

$tar = new Archive_Tar($outputFile, $this->compression);

$fileList = $this->listFilesToTar($this->directory);

$result = $tar->create($fileList);
\chdir($workingDir);
return $result;
}
}
45 changes: 45 additions & 0 deletions lib/Release/Bundler/ZipBundler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php
namespace ParagonIE\Gossamer\Release\Bundler;

use Exception;
use ParagonIE\Gossamer\GossamerException;
use ZipArchive;

class ZipBundler extends AbstractBundler
{
/**
* @param ZipArchive $zip
* @param string $parent
* @return ZipArchive
*/
public function addFilesToZip(ZipArchive $zip, $parent)
{
$base = str_replace($this->directory, '', $parent);
$zip->addEmptyDir($base);
/** @var string $file */
foreach (glob($parent . '/*') as $file) {
if (is_dir($file)) {
$this->addFilesToZip($zip, $file);
} else {
$newFile = ltrim(str_replace($this->directory, '', $file), '/');
$zip->addFile((string) $file, (string) $newFile);
}
}
return $zip;
}

public function bundle($outputFile)
{
$workingDir = \getcwd();
\chdir($this->directory);
$zip = new ZipArchive();
if ($zip->open($outputFile, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
throw new GossamerException("Cannot create zip");
}

$this->addFilesToZip($zip, $this->directory);

\chdir($workingDir);
return $zip->close();
}
}
2 changes: 1 addition & 1 deletion lib/Client/TypeHelperTrait.php → lib/TypeHelperTrait.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?php
namespace ParagonIE\Gossamer\Client;
namespace ParagonIE\Gossamer;

use Exception;

Expand Down
Loading

0 comments on commit 78a81b8

Please sign in to comment.