Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow changelog to be written to a file. #6

Merged
merged 1 commit into from
May 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,19 @@ You can install with composer:
Generate a change log based on a GitHub milestone with the following command:

$ ./vendor/bin/changelog-generator generate --user=doctrine --repository=migrations --milestone=2.0

### Write to File

Write the generated changelog to a file with the `--file` option. If you don't provide a value, it will be written
to the current working directory in a file named `CHANGELOG.md`:

$ ./vendor/bin/changelog-generator generate --user=doctrine --repository=migrations --milestone=2.0 --file

You can pass a value to `--file` to specify where the changelog file should be written:

$ ./vendor/bin/changelog-generator generate --user=doctrine --repository=migrations --milestone=2.0 --file=changelog.md

By default it will overwrite the file contents but you can pass the `--append` option to append the changelog to
the existing contents.

$ ./vendor/bin/changelog-generator generate --user=doctrine --repository=migrations --milestone=2.0 --file=changelog.md --append
4 changes: 4 additions & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@

<rule ref="Doctrine"/>

<rule ref="Squiz.Classes.ClassFileName.NoMatch">
<exclude-pattern>*/tests/*</exclude-pattern>
</rule>

<rule ref="PSR1.Classes.ClassDeclaration.MultipleClasses">
<exclude-pattern>*/tests/*</exclude-pattern>
</rule>
Expand Down
68 changes: 67 additions & 1 deletion src/ChangelogGenerator/Command/GenerateChangelogCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@
namespace ChangelogGenerator\Command;

use ChangelogGenerator\ChangelogGenerator;
use InvalidArgumentException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\StreamOutput;
use function fopen;
use function getcwd;
use function sprintf;

class GenerateChangelogCommand extends Command
{
Expand Down Expand Up @@ -51,6 +56,19 @@ protected function configure() : void
InputOption::VALUE_REQUIRED,
'The milestone to build the changelog for.'
)
->addOption(
'file',
null,
InputOption::VALUE_OPTIONAL,
'Write the changelog to a file.',
false
)
->addOption(
'append',
null,
InputOption::VALUE_NONE,
'Append the changelog to the file.'
)
;
}

Expand All @@ -60,6 +78,54 @@ protected function execute(InputInterface $input, OutputInterface $output) : voi
$repository = $input->getOption('repository');
$milestone = $input->getOption('milestone');

$this->changelogGenerator->generate($user, $repository, $milestone, $output);
$changelogOutput = $this->getChangelogOutput($input, $output);

$this->changelogGenerator->generate($user, $repository, $milestone, $changelogOutput);
}

/**
* @return false|resource
*/
protected function fopen(string $file, string $mode)
{
return fopen($file, $mode);
}

/**
* @throws InvalidArgumentException
*/
protected function createStreamOutput(string $file, bool $append) : StreamOutput
{
$handle = $this->fopen($file, $this->getFileHandleMode($append));

if ($handle === false) {
throw new InvalidArgumentException(sprintf('Could not open handle for %s', $file));
}

return new StreamOutput($handle);
}

private function getFileHandleMode(bool $append) : string
{
return $append ? 'a+' : 'w+';
}

private function getChangelogOutput(InputInterface $input, OutputInterface $output) : OutputInterface
{
$file = $input->getOption('file');
$append = (bool) $input->getOption('append');

$changelogOutput = $output;

if ($file !== false) {
$changelogOutput = $this->createStreamOutput($this->getChangelogFilePath($file), $append);
}

return $changelogOutput;
}

private function getChangelogFilePath(?string $file) : string
{
return $file === null ? sprintf('%s/CHANGELOG.md', getcwd()) : $file;
}
}
140 changes: 134 additions & 6 deletions tests/ChangelogGenerator/Tests/Functional/ConsoleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,44 +6,172 @@

use ChangelogGenerator\ChangelogGenerator;
use ChangelogGenerator\Command\GenerateChangelogCommand;
use InvalidArgumentException;
use PackageVersions\Versions;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\StreamOutput;
use function sprintf;
use function sys_get_temp_dir;
use function unlink;

final class ConsoleTest extends TestCase
{
/** @var \PHPUnit_Framework_MockObject_MockObject|ChangelogGenerator */
private $changelogGenerator;

/** @var \PHPUnit_Framework_MockObject_MockObject|GenerateChangelogCommand */
private $generateChangelogCommand;

/** @var Application */
private $application;

public function testGenerate() : void
{
$input = new ArrayInput([
'command' => 'generate',
'--user' => 'jwage',
'--repository' => 'changelog-generator',
'--milestone' => '1.0',
'command' => 'generate',
'--user' => 'jwage',
'--repository' => 'changelog-generator',
'--milestone' => '1.0',
]);

$output = $this->createMock(OutputInterface::class);

$this->changelogGenerator->expects($this->once())
->method('generate')
->with('jwage', 'changelog-generator', '1.0');
->with('jwage', 'changelog-generator', '1.0', $output);

$this->application->run($input, $output);
}

public function testGenerateFile() : void
{
$input = new ArrayInput([
'command' => 'generate',
'--user' => 'jwage',
'--repository' => 'changelog-generator',
'--milestone' => '1.0',
'--file' => null,
]);

$output = $this->createMock(OutputInterface::class);
$streamOutput = $this->createMock(StreamOutput::class);

$this->generateChangelogCommand->expects($this->once())
->method('createStreamOutput')
->willReturn($streamOutput);

$this->changelogGenerator->expects($this->once())
->method('generate')
->with('jwage', 'changelog-generator', '1.0', $streamOutput);

$this->application->run($input, $output);
}

public function testGenerateFilePathGiven() : void
{
$input = new ArrayInput([
'command' => 'generate',
'--user' => 'jwage',
'--repository' => 'changelog-generator',
'--milestone' => '1.0',
'--file' => 'CHANGELOG.md',
]);

$output = $this->createMock(OutputInterface::class);
$streamOutput = $this->createMock(StreamOutput::class);

$this->generateChangelogCommand->expects($this->once())
->method('createStreamOutput')
->willReturn($streamOutput);

$this->changelogGenerator->expects($this->once())
->method('generate')
->with('jwage', 'changelog-generator', '1.0', $streamOutput);

$this->application->run($input, $output);
}

public function testGenerateFileAppend() : void
{
$input = new ArrayInput([
'command' => 'generate',
'--user' => 'jwage',
'--repository' => 'changelog-generator',
'--milestone' => '1.0',
'--file' => 'CHANGELOG.md',
'--append' => true,
]);

$output = $this->createMock(OutputInterface::class);
$streamOutput = $this->createMock(StreamOutput::class);

$this->generateChangelogCommand->expects($this->once())
->method('createStreamOutput')
->willReturn($streamOutput);

$this->changelogGenerator->expects($this->once())
->method('generate')
->with('jwage', 'changelog-generator', '1.0', $streamOutput);

$this->application->run($input, $output);
}

public function testCreateStreamOutput() : void
{
$generateChangelogCommand = new GenerateChangelogCommandStub($this->changelogGenerator);

$file = sprintf('%s/test.md', sys_get_temp_dir());

self::assertInstanceOf(StreamOutput::class, $generateChangelogCommand->createStreamOutputTest($file, true));
self::assertInstanceOf(StreamOutput::class, $generateChangelogCommand->createStreamOutputTest($file, false));

unlink($file);
}

public function testCreateStreamOutputCouldNotOpenHandleInvalidArgumentException() : void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Could not open handle for /tmp/test.md');

$file = sprintf('%s/test.md', sys_get_temp_dir());

/** @var \PHPUnit_Framework_MockObject_MockObject|GenerateChangelogCommandStub $generateChangelogCommand */
$generateChangelogCommand = $this->getMockBuilder(GenerateChangelogCommandStub::class)
->setConstructorArgs([$this->changelogGenerator])
->setMethods(['fopen'])
->getMock();

$generateChangelogCommand->expects($this->once())
->method('fopen')
->with($file, 'a+')
->willReturn(false);

$generateChangelogCommand->createStreamOutputTest($file, true);
}

protected function setUp() : void
{
$this->changelogGenerator = $this->createMock(ChangelogGenerator::class);

$this->application = new Application('Changelog Generator', Versions::getVersion('jwage/changelog-generator'));
$this->application->setAutoExit(false);
$this->application->add(new GenerateChangelogCommand($this->changelogGenerator));

$this->generateChangelogCommand = $this->getMockBuilder(GenerateChangelogCommand::class)
->setConstructorArgs([$this->changelogGenerator])
->setMethods(['createStreamOutput'])
->getMock();

$this->application->add($this->generateChangelogCommand);
}
}

class GenerateChangelogCommandStub extends GenerateChangelogCommand
{
public function createStreamOutputTest(string $file, bool $append) : StreamOutput
{
return $this->createStreamOutput($file, $append);
}
}