Skip to content

Commit

Permalink
support pull request pipelines
Browse files Browse the repository at this point in the history
in december 2018 atlassian announced pull request pipelines. this change
set makes them visible w/ --show and executable via --pipeline and
--trigger with pr:<branch-name>.

additionally reorganize yaml test data into a folder of it's own.
  • Loading branch information
ktomk committed Apr 2, 2019
1 parent 6154f77 commit a6d6592
Show file tree
Hide file tree
Showing 23 changed files with 110 additions and 44 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Added
- Suggestion to install the PHP YAML extension
- Kept containers are automatically re-used if they still exist
- Support for pull request pipelines
### Changed
- Reduce artifact chunk size from fixed number 1792 to string
length based.
Expand Down
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@ information about the pipelines is available via `--show`. Both

Run the pipeline as if a tag/branch or bookmark has been pushed
with `--trigger <ref>` where `<ref>` is `tag:<name>`,
`branch:<name>` or `bookmark:<name>`. If there is no tag, branch
or bookmark pipeline with that name, the name is compared against
the patterns of the referenced pipelines type and if found, that
pipeline is run. Otherwise the default pipeline is run, if there
is no default pipeline, no pipeline at all is run and the command
exits with non-zero status.
`branch:<name>`, `bookmark:<name>` or `pr:<branch-name>`. If
there is no tag, branch, bookmark or pull-request pipeline with
that name, the name is compared against the patterns of the
referenced type and if found, that pipeline is run.

Otherwise the default pipeline is run, if there is no default
pipeline, no pipeline at all is run and the command exits with
non-zero status.

`--pipeline` and `--trigger` can be used together, `--pipeline`
overrides pipeline from `--trigger` but `--trigger` still
Expand Down
14 changes: 10 additions & 4 deletions src/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ class File
*/
private $pipelines;

/**
* @var array
*/
private static $sections = array('branches', 'tags', 'bookmarks', 'pull-requests', 'custom');

/**
* File constructor.
*
Expand Down Expand Up @@ -282,7 +287,7 @@ public function getIdFrom(Pipeline $pipeline)

private function isIdValid($id)
{
return (bool)preg_match('~^(default|(branches|tags|bookmarks|custom)/[^\x00-\x1F\x7F-\xFF]*)$~', $id);
return (bool)preg_match('~^(default|(' . implode('|', self::$sections) . ')/[^\x00-\x1F\x7F-\xFF]*)$~', $id);
}

/**
Expand Down Expand Up @@ -332,15 +337,16 @@ private function searchIdByTypeReference($type, $reference)
private function parsePipelineReferences(array &$array)
{
// quick validation: pipeline sections
$sections = array('branches', 'tags', 'bookmarks', 'custom');
$sections = self::$sections;
$count = 0;
foreach ($sections as $section) {
if (isset($array[$section])) {
$count++;
}
}
if (!$count && !isset($array['default'])) {
ParseException::__("'pipelines' requires at least a default, branches, tags, bookmarks or custom section");
$middle = implode(', ', array_slice($sections, 0, -1));
ParseException::__("'pipelines' requires at least a default, ${middle} or custom section");
}

$references = array();
Expand Down Expand Up @@ -382,7 +388,7 @@ private function parsePipelineReferences(array &$array)
*/
private function validateType($type)
{
$scopes = array('branches', 'tags', 'bookmarks');
$scopes = array_slice(self::$sections, 0, 4);
if (!in_array($type, $scopes, true)) {
throw new InvalidArgumentException(sprintf("Invalid type '%s'", $type));
}
Expand Down
5 changes: 5 additions & 0 deletions src/Runner/Env.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
/* this file is part of pipelines */

namespace Ktomk\Pipelines\Runner;

use Ktomk\Pipelines\Cli\Args\Args;
use Ktomk\Pipelines\Cli\Args\Collector;
use Ktomk\Pipelines\Lib;
Expand Down Expand Up @@ -105,8 +106,12 @@ public function addReference(Reference $ref)
'bookmark' => 'BITBUCKET_BOOKMARK',
'branch' => 'BITBUCKET_BRANCH',
'tag' => 'BITBUCKET_TAG',
'pr' => 'BITBUCKET_BRANCH',
);

if (!isset($map[$type])) {
throw new \UnexpectedValueException(sprintf('Unknown reference type: "%s"', $type));
}
$var = $map[$type];

if (!isset($this->vars[$var])) {
Expand Down
7 changes: 4 additions & 3 deletions src/Runner/Reference.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

class Reference
{
const BRANCH_TAG_BOOKMARK = '~^(branch|tag|bookmark):(.+)$~';
const TRIGGER = '~^(branch|tag|bookmark|pr):(.+)$~';

private $string;

Expand All @@ -19,6 +19,7 @@ class Reference
private static $map = array(
'bookmark' => 'bookmarks',
'branch' => 'branches',
'pr' => 'pull-requests',
'tag' => 'tags',
);

Expand Down Expand Up @@ -51,7 +52,7 @@ public static function create($string = null)
*/
public static function valid($string)
{
$result = preg_match(self::BRANCH_TAG_BOOKMARK, $string);
$result = preg_match(self::TRIGGER, $string);

return (bool)$result;
}
Expand Down Expand Up @@ -92,7 +93,7 @@ private function parse($string)
return;
}

$result = preg_match(self::BRANCH_TAG_BOOKMARK, $string, $matches);
$result = preg_match(self::TRIGGER, $string, $matches);

if (!$result) {
throw new InvalidArgumentException(sprintf('invalid reference: "%s"', $string));
Expand Down
6 changes: 3 additions & 3 deletions src/Utility/Help.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ public function showHelp()
looking up the <basename> file from
the current working directory
--trigger <ref> build trigger, <ref> can be of either
tag:<name>, branch:<name> or
bookmark:<name>. used in determination
of the pipeline to run
tag:<name>, branch:<name>,
bookmark:<name> or pr:<branch-name>.
determines the pipeline to run
--pipeline <id> run pipeline with <id>, see --list
--verbatim only give verbatim output of the
pipeline, no other information around
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ pipelines:
- step:
script:
- echo "bookmark v1.0.0"
-
pull-requests:
'**':
- step:
name: default pull request pipeline
script:
- ":"

custom:
lint-php: # lint against diverse PHP versions
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
14 changes: 14 additions & 0 deletions tests/data/yml/pull-requests-pipeline.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
# this file is part of pipelines
#
# fixture bitbucket-pipelines.yml w/ pull-requests
image: php:5.6
clone:
depth: full
pipelines:
pull-requests:
'**':
- step:
name: default pull request pipeline
script:
- ":"
2 changes: 1 addition & 1 deletion tests/integration/File/ImageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public function provideCommands()
*/
public function testSuccessfulCommands($command)
{
$this->assert("bin/pipelines --file tests/data/images.yml ${command}");
$this->assert("bin/pipelines --file tests/data/yml/images.yml ${command}");
}

private function assert($command)
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/YamlReferenceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class YamlReferenceTest extends TestCase
{
public function testFileWithAliasParses()
{
$file = File::createFromFile(__DIR__ . '/../data/alias.yml');
$file = File::createFromFile(__DIR__ . '/../data/yml/alias.yml');
$actual = $file->getPipelineIds();
$idDefault = 'default';
$idAlias = 'branches/feature/*';
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/File/ReferenceSearchTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class ReferenceSearchTest extends TestCase
protected function setUp()
{
parent::setUp();
$this->file = File::createFromFile(__DIR__ . '/../../data/bitbucket-pipelines.yml');
$this->file = File::createFromFile(__DIR__ . '/../../data/yml/bitbucket-pipelines.yml');
}

public function searchReference($ref = null)
Expand Down
18 changes: 9 additions & 9 deletions tests/unit/FileTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function testCreateFromFile()
*/
public function testCreateFromFileWithError()
{
$path = __DIR__ . '/../data/error.yml';
$path = __DIR__ . '/../data/yml/error.yml';

File::createFromFile($path);
}
Expand All @@ -42,7 +42,7 @@ public function testCreateFromFileWithError()
*/
public function testCreateFromFileWithInvalidId()
{
$path = __DIR__ . '/../data/invalid-pipeline-id.yml';
$path = __DIR__ . '/../data/yml/invalid-pipeline-id.yml';

$file = File::createFromFile($path);

Expand Down Expand Up @@ -160,11 +160,11 @@ public function testImageNameRequired()

public function testGetPipelineIds()
{
$file = File::createFromFile(__DIR__ . '/../data/bitbucket-pipelines.yml');
$file = File::createFromFile(__DIR__ . '/../data/yml/bitbucket-pipelines.yml');
$ids = $file->getPipelineIds();
$this->assertInternalType('array', $ids);
$this->assertArrayHasKey(11, $ids);
$this->assertSame('custom/unit-tests', $ids[11]);
$this->assertArrayHasKey(12, $ids);
$this->assertSame('custom/unit-tests', $ids[12]);
}

/**
Expand All @@ -183,7 +183,7 @@ public function testGetPipelines(File $file)

public function testGetReference()
{
$file = File::createFromFile(__DIR__ . '/../data/bitbucket-pipelines.yml');
$file = File::createFromFile(__DIR__ . '/../data/yml/bitbucket-pipelines.yml');

$pipeline = $file->getById('branches/master');
$this->assertNotNull($pipeline);
Expand Down Expand Up @@ -231,7 +231,7 @@ public function testFlyweightPatternWithPatternSection()
*/
public function testInvalidReferenceName()
{
File::createFromFile(__DIR__ . '/../data/bitbucket-pipelines.yml')
File::createFromFile(__DIR__ . '/../data/yml/bitbucket-pipelines.yml')
->getById('branch/master'); # must be branch_es_
}

Expand Down Expand Up @@ -264,7 +264,7 @@ public function testNoListInBranchesSectionException()

public function testSearchReference()
{
$file = File::createFromFile(__DIR__ . '/../data/bitbucket-pipelines.yml');
$file = File::createFromFile(__DIR__ . '/../data/yml/bitbucket-pipelines.yml');

$pipeline = $file->searchTypeReference('branches', 'master');
$this->asPlFiStName('master duplicate', $pipeline, 'direct match');
Expand Down Expand Up @@ -340,7 +340,7 @@ public function testNoDefaultAsFallBack()
*/
public function testSearchReferenceInvalidScopeException()
{
$file = File::createFromFile(__DIR__ . '/../data/bitbucket-pipelines.yml');
$file = File::createFromFile(__DIR__ . '/../data/yml/bitbucket-pipelines.yml');
$file->searchTypeReference('invalid', '');
}

Expand Down
32 changes: 30 additions & 2 deletions tests/unit/Runner/EnvTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
namespace Ktomk\Pipelines\Runner;

use Ktomk\Pipelines\Cli\Args\ArgsTester;
use PHPUnit\Framework\TestCase;
use Ktomk\Pipelines\UnitTestCase;

/**
* @covers \Ktomk\Pipelines\Runner\Env
*/
class EnvTest extends TestCase
class EnvTest extends UnitTestCase
{
public function testCreation()
{
Expand Down Expand Up @@ -101,6 +101,18 @@ public function testAddRefType()
$this->assertCount($default + 2, $env->getArgs('-e'), 'full reference does add variables');
}

public function testPullRequestAddsBranchName()
{
$env = Env::create();
$default = count($env->getArgs('-e'));

$env->addReference(Reference::create());
$this->assertCount($default, $env->getArgs('-e'), 'null reference does not add any variables');

$env->addReference(Reference::create('pr:feature'));
$this->assertCount($default + 2, $env->getArgs('-e'), 'full reference does add variables');
}

public function testAddRefTypeIfSet()
{
$env = Env::create(array('BITBUCKET_TAG' => 'inherit'));
Expand Down Expand Up @@ -251,4 +263,20 @@ public function testGetResolver()
$env->getResolver()
);
}

/**
* @expectedException \UnexpectedValueException
* @expectedExceptionMessage Unknown reference type: "foo"
*/
public function testAddReferenceOfUnknownType()
{
$env = new Env();

$reference = $this->createMock('\Ktomk\Pipelines\Runner\Reference');
$reference
->method('getType')
->willReturn('foo');

$env->addReference($reference);
}
}
4 changes: 3 additions & 1 deletion tests/unit/Runner/ReferenceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public function provideRefs()
array('tag:', false),
array('tag:1.0.0', true),
array('branch:feature/drop-support', true),
array('pr:feature-42', true),
);
}

Expand All @@ -41,7 +42,7 @@ public function provideRefs()
*/
public function testValidation($string, $valid)
{
$this->assertSame($valid, Reference::valid($string));
$this->assertSame($valid, Reference::valid($string), sprintf('reference "%s"', $string));
}

/**
Expand Down Expand Up @@ -87,5 +88,6 @@ public function testGetPipelinesType()
$this->assertSame('bookmarks', $f('bookmark:stable'));
$this->assertSame('branches', $f('branch:master'));
$this->assertSame('tags', $f('tag:1.0.0'));
$this->assertSame('pull-requests', $f('pr:feature'));
}
}
6 changes: 3 additions & 3 deletions tests/unit/Utility/AppTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public function testMainExceptionHandlingParseException()
{
$app = new App(new Streams());
$actual = $app->main(array(
'cmd', '--file', 'tests/data/invalid-pipeline.yml',
'cmd', '--file', 'tests/data/yml/invalid-pipeline.yml',
'--pipeline', 'custom/unit-tests',
));
$this->assertSame(2, $actual);
Expand Down Expand Up @@ -113,15 +113,15 @@ public function testInvalidWorkingDirStatus()
public function testNoPipelineToRunStatus()
{
$app = new App(new Streams());
$actual = $app->main(array('cmd', '--file', 'tests/data/no-default-pipeline.yml'));
$actual = $app->main(array('cmd', '--file', 'tests/data/yml/no-default-pipeline.yml'));
$this->assertSame(1, $actual);
}

public function testShowPipelinesWithError()
{
$this->expectOutputRegex('{ERROR\s+\'image\' invalid Docker image}');
$app = new App(new Streams(null, 'php://output'));
$actual = $app->main(array('cmd', '--file', 'tests/data/invalid-pipeline.yml', '--show'));
$actual = $app->main(array('cmd', '--file', 'tests/data/yml/invalid-pipeline.yml', '--show'));
$this->assertSame(1, $actual);
}

Expand Down
Loading

0 comments on commit a6d6592

Please sign in to comment.