Skip to content
Closed
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
37 changes: 2 additions & 35 deletions src/Plugins/Snapshot.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Pest\Plugins;

use Pest\Contracts\Plugins\HandlesArguments;
use Pest\Support\Ci;
use Pest\TestSuite;

/**
Expand All @@ -16,34 +17,9 @@ final class Snapshot implements HandlesArguments

public static bool $updateSnapshots = false;

/**
* @var list<string>
*/
private const array CI_ENVIRONMENT_VARIABLES = [
'CI',
'GITHUB_ACTIONS',
'GITLAB_CI',
'CIRCLECI',
'TRAVIS',
'APPVEYOR',
'BITBUCKET_BUILD_NUMBER',
'BUILDKITE',
'TEAMCITY_VERSION',
'JENKINS_URL',
'SYSTEM_COLLECTIONURI',
'CI_NAME',
'TASKCLUSTER_ROOT_URL',
'DRONE',
'WERCKER',
'NEVERCODE',
'SEMAPHORE',
'NETLIFY',
'NOW_BUILDER',
];

public static function shouldCreateMissingSnapshots(): bool
{
return self::$updateSnapshots || ! self::runningOnCI();
return self::$updateSnapshots || ! Ci::isRunning();
}

/**
Expand Down Expand Up @@ -149,13 +125,4 @@ private function isFullRun(array $arguments): bool

return true;
}

private static function runningOnCI(): bool
{
if (Environment::name() === Environment::CI) {
return true;
}

return array_any(self::CI_ENVIRONMENT_VARIABLES, fn (string $environmentVariable): bool => getenv($environmentVariable) !== false);
}
}
34 changes: 17 additions & 17 deletions src/Plugins/Tia.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
use Pest\Plugins\Concerns\HandleArguments;
use Pest\Plugins\Tia\BaselineSync;
use Pest\Plugins\Tia\ChangedFiles;
use Pest\Plugins\Tia\CiDefaultBranch;
use Pest\Plugins\Tia\CiBranch;
use Pest\Plugins\Tia\Contracts\State;
use Pest\Plugins\Tia\CoverageCollector;
use Pest\Plugins\Tia\Fingerprint;
Expand Down Expand Up @@ -196,8 +196,6 @@ final class Tia implements AddsOutput, HandlesArguments, HandlesOriginalArgument

private bool $unreadableGraphReported = false;

private bool $detachedHead = false;

private bool $graphUnreachable = false;

private bool $fullSuiteFallbackRan = false;
Expand Down Expand Up @@ -283,19 +281,11 @@ private function discardUnreadableGraph(): void

private function deleteState(string $key): bool
{
if ($this->detachedHead) {
return false;
}

return $this->state->delete($key);
}

private function saveGraph(Graph $graph): bool
{
if ($this->detachedHead) {
return true;
}

$json = $graph->encode();

if ($json === null) {
Expand Down Expand Up @@ -851,7 +841,7 @@ private function handleParent(array $arguments, string $projectRoot, bool $force
$fingerprint = Fingerprint::compute($projectRoot);
$this->startFingerprint = $fingerprint;

if ($forceRebuild && ! $this->detachedHead) {
if ($forceRebuild) {
Storage::purge($projectRoot);
}

Expand Down Expand Up @@ -1950,10 +1940,17 @@ private function resolveBranch(string $projectRoot): void

Parallel::setGlobal(self::FALLBACK_BRANCH_GLOBAL, $this->fallbackBranch);

$currentBranch = $changedFiles->currentBranch();
$this->branch = $changedFiles->currentBranch()
?? CiBranch::detectCurrent()
?? $this->workspaceBranch($projectRoot);
}

$this->detachedHead = $currentBranch === null;
$this->branch = $currentBranch ?? $this->fallbackBranch;
private function workspaceBranch(string $projectRoot): string
{
$realProjectRoot = realpath($projectRoot);
$hash = hash('sha256', $realProjectRoot === false ? $projectRoot : $realProjectRoot);

return Graph::WORKSPACE_BRANCH_PREFIX.substr($hash, 0, 16);
}

private function resolveFallbackBranch(ChangedFiles $changedFiles): ?string
Expand All @@ -1965,7 +1962,7 @@ private function resolveFallbackBranch(ChangedFiles $changedFiles): ?string
}

return $this->watchPatterns->defaultBranch()
?? CiDefaultBranch::detect()
?? CiBranch::detectDefault()
?? $changedFiles->defaultBranch()
?? $this->soleRecordedBranch();
}
Expand All @@ -1978,7 +1975,10 @@ private function soleRecordedBranch(): ?string
return null;
}

$branches = Graph::branchesIn($json);
$branches = array_values(array_filter(
Graph::branchesIn($json),
fn (string $branch): bool => ! str_starts_with($branch, Graph::WORKSPACE_BRANCH_PREFIX),
));

return count($branches) === 1 ? $branches[0] : null;
}
Expand Down
10 changes: 2 additions & 8 deletions src/Plugins/Tia/BaselineSync.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Pest\Panic;
use Pest\Plugins\Tia;
use Pest\Plugins\Tia\Contracts\State;
use Pest\Support\Ci;
use Pest\Support\View;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Process;
Expand Down Expand Up @@ -164,7 +165,7 @@ private function formatDuration(int $seconds): string

private function emitPublishInstructions(): void
{
if ($this->isCi()) {
if (Ci::isRunning()) {
$this->renderBadge('INFO', 'No baseline yet — this run will produce one.');

return;
Expand All @@ -174,13 +175,6 @@ private function emitPublishInstructions(): void
$this->renderChild('See https://pestphp.com/docs/tia for how to publish one from CI.');
}

private function isCi(): bool
{
return getenv('GITHUB_ACTIONS') === 'true'
|| getenv('GITLAB_CI') === 'true'
|| getenv('CIRCLECI') === 'true';
}

private function detectGitHubRepo(string $projectRoot): ?string
{
$gitConfig = $projectRoot.DIRECTORY_SEPARATOR.'.git'.DIRECTORY_SEPARATOR.'config';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
/**
* @internal
*/
final class CiDefaultBranch
final class CiBranch
{
/**
* @var array<int, class-string<Ci>>
Expand All @@ -19,7 +19,20 @@ final class CiDefaultBranch
Cis\GitHub::class,
];

public static function detect(): ?string
public static function detectCurrent(): ?string
{
foreach (self::CIS as $class) {
$branch = (new $class)->currentBranch();

if ($branch !== null) {
return $branch;
}
}

return null;
}

public static function detectDefault(): ?string
{
foreach (self::CIS as $class) {
$branch = (new $class)->defaultBranch();
Expand Down
5 changes: 5 additions & 0 deletions src/Plugins/Tia/Cis/GitHub.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
{
use ReadsEnvironment;

public function currentBranch(): ?string
{
return $this->environment('GITHUB_REF_NAME');
}

public function defaultBranch(): ?string
{
$path = $this->environment('GITHUB_EVENT_PATH');
Expand Down
5 changes: 5 additions & 0 deletions src/Plugins/Tia/Cis/GitLab.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
{
use ReadsEnvironment;

public function currentBranch(): ?string
{
return $this->environment('CI_COMMIT_BRANCH');
}

public function defaultBranch(): ?string
{
return $this->environment('CI_DEFAULT_BRANCH');
Expand Down
2 changes: 2 additions & 0 deletions src/Plugins/Tia/Contracts/Ci.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@
*/
interface Ci
{
public function currentBranch(): ?string;

public function defaultBranch(): ?string;
}
6 changes: 6 additions & 0 deletions src/Plugins/Tia/Graph.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
*/
final class Graph
{
public const string WORKSPACE_BRANCH_PREFIX = '@workspace:';

/**
* @var array<string, string>
*/
Expand Down Expand Up @@ -1632,6 +1634,10 @@ public function pruneMissingBranches(array $keep): void
$survivors = array_fill_keys($keep, true);

foreach (array_keys($this->baselines) as $branch) {
if (str_starts_with($branch, self::WORKSPACE_BRANCH_PREFIX)) {
continue;
}

if (! isset($survivors[$branch])) {
unset($this->baselines[$branch]);
}
Expand Down
47 changes: 47 additions & 0 deletions src/Support/Ci.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace Pest\Support;

use Pest\Plugins\Environment;

/**
* @internal
*/
final class Ci
{
/**
* @var list<string>
*/
private const array ENVIRONMENT_VARIABLES = [
'CI',
'GITHUB_ACTIONS',
'GITLAB_CI',
'CIRCLECI',
'TRAVIS',
'APPVEYOR',
'BITBUCKET_BUILD_NUMBER',
'BUILDKITE',
'TEAMCITY_VERSION',
'JENKINS_URL',
'SYSTEM_COLLECTIONURI',
'CI_NAME',
'TASKCLUSTER_ROOT_URL',
'DRONE',
'WERCKER',
'NEVERCODE',
'SEMAPHORE',
'NETLIFY',
'NOW_BUILDER',
];

public static function isRunning(): bool
{
if (Environment::name() === Environment::CI) {
return true;
}

return array_any(self::ENVIRONMENT_VARIABLES, fn (string $env): bool => getenv($env) !== false);
}
}
9 changes: 6 additions & 3 deletions tests/Features/Tia/BranchShapes.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
->and($delta->isResultsOnly())->toBeTrue($delta->summary());
})->skipOnWindows();

test('a detached HEAD does not reclaim anything either', function (): void {
test('a detached HEAD reclaims missing branches without changing the default baseline', function (): void {
$project = Project::make('master');
$project->seed('master');

Expand All @@ -130,8 +130,11 @@
$project->pest('--tia');
$delta = $project->delta();

expect($project->branchKeys())->toBe(['master', 'feature-x'])
->and($delta->isHardSuppressed())->toBeTrue($delta->summary());
expect($project->branchKeys())->toHaveCount(2)
->and($project->branchKeys())->toContain('master')
->and($project->branchKeys())->not->toContain('feature-x')
->and($delta->baselineUntouched('master'))->toBeTrue($delta->summary())
->and($delta->structureMoved())->toBeTrue($delta->summary());
})->skipOnWindows();

test('the default branch baseline survives every branch that comes and goes', function (): void {
Expand Down
33 changes: 30 additions & 3 deletions tests/Features/Tia/DefaultBranchWriteTier.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,17 +104,20 @@
->and($delta->isHardSuppressed())->toBeTrue($delta->summary());
})->skipOnWindows();

test('a detached HEAD replays without minting a branch key', function (): void {
test('a detached HEAD replays through a workspace baseline', function (): void {
$project = Project::make('master');
$project->seed('master');

$project->git()->detach();

$result = $project->pest('--tia');
$hasWorkspaceBranch = array_any($project->branchKeys(), fn (string $branch): bool => str_starts_with($branch, '@workspace:'));

expect($result->replayed())->toBe(Project::TOTAL_TESTS, $result->describe())
->and($result->uncached())->toBe(0, $result->describe())
->and($project->branchKeys())->toBe(['master']);
->and($project->branchKeys())->toHaveCount(2)
->and($project->branchKeys())->toContain('master')
->and($hasWorkspaceBranch)->toBeTrue();
})->skipOnWindows();

test('a detached HEAD does not write into the default branch baseline', function (array $arguments): void {
Expand All @@ -125,9 +128,13 @@
$project->pest(...$arguments);

$delta = $project->delta();
$hasWorkspaceBranch = array_any($project->branchKeys(), fn (string $branch): bool => str_starts_with($branch, '@workspace:'));

expect($delta->baselineUntouched('master'))->toBeTrue($delta->summary())
->and($project->branchKeys())->toBe(['master']);
->and($project->branchKeys())->toHaveCount(2)
->and($project->branchKeys())->toContain('master')
->and($hasWorkspaceBranch)->toBeTrue()
->and($delta->writtenCount())->toBe(0, $delta->summary());
})->with([
'sequential' => [['--filter=adds two numbers']],
'parallel' => [['--parallel', '--processes=2', '--filter=adds two numbers']],
Expand All @@ -147,6 +154,26 @@
->and($delta->writtenCount())->toBe(0, $delta->summary());
})->skipOnWindows();

test('a detached CI branch writes into its branch baseline', function (array $environment): void {
$project = Project::make('master');
$project->seed('master');

$project->git()->detach();
$project->pestWithEnvironment($project->path(), $environment, '--filter=adds two numbers');

expect($project->branchKeys())->toBe(['master', 'feature-x'])
->and(array_any($project->branchKeys(), fn (string $branch): bool => str_starts_with($branch, '@workspace:')))->toBeFalse();
})->with([
'GitLab' => [[
'GITLAB_CI' => 'true',
'CI_COMMIT_BRANCH' => 'feature-x',
]],
'GitHub' => [[
'GITHUB_ACTIONS' => 'true',
'GITHUB_REF_NAME' => 'feature-x',
]],
])->skipOnWindows();

test('the fallback reaches parallel workers', function (): void {
$project = Project::make('master');
$project->seed('master');
Expand Down
Loading
Loading