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

fix(fingerprint): Add $global Parameter to Fingerprint Functions and Path Handling Fixes #527

Merged
merged 1 commit into from
Sep 18, 2024
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
1 change: 1 addition & 0 deletions bin/generate-tests.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
'qa:phpstan:update',
// Customized tests
'fingerprint:task-with-a-fingerprint-and-force',
'fingerprint:task-with-a-fingerprint-global',
'fingerprint:task-with-a-fingerprint',
'fingerprint:task-with-complete-fingerprint-check',
'log:all-level',
Expand Down
4 changes: 4 additions & 0 deletions doc/going-further/helpers/fingerprint.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ function task_with_a_fingerprint(): void
> You can use the `$force` parameter of the `fingerprint()` function to force
> the execution of the callback even if the fingerprint has not changed.

> [!NOTE]
> By default the fingerprint is scoped to the current project, but you can use
> the `$global` parameter to make it shared across all projects.

## The `hasher()` function

Most of the time, you will want your fingerprint hash to be based on the content
Expand Down
17 changes: 17 additions & 0 deletions examples/fingerprint.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,23 @@ function task_with_a_fingerprint(): void
io()->writeln('Cool! I finished!');
}

#[AsTask(description: 'Execute a callback only if the global fingerprint has changed (Shared across all projects)')]
function task_with_a_fingerprint_global(): void
{
io()->writeln('Hello Task with Global Fingerprint!');

fingerprint(
callback: function () {
io()->writeln('Cool, no global fingerprint! Executing...');
},
id: 'my_global_fingerprint_check',
fingerprint: my_fingerprint_check(),
global: true, // This ensures that the fingerprint is shared across all projects (not only the current one)
);

io()->writeln('Cool! I finished global!');
}

#[AsTask(description: 'Check if the fingerprint has changed before executing some code')]
function task_with_complete_fingerprint_check(): void
{
Expand Down
22 changes: 18 additions & 4 deletions src/Fingerprint/FingerprintHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Castor\Fingerprint;

use Castor\Helper\PathHelper;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Contracts\Cache\CacheInterface;

Expand All @@ -15,9 +16,9 @@ public function __construct(
) {
}

public function verifyFingerprintFromHash(string $id, string $fingerprint): bool
public function verifyFingerprintFromHash(string $id, string $fingerprint, bool $global = false): bool
{
$itemKey = $id . self::SUFFIX;
$itemKey = $this->getItemKey($id, $global);

if (false === $this->cache->hasItem($itemKey)) {
return false;
Expand All @@ -36,9 +37,9 @@ public function verifyFingerprintFromHash(string $id, string $fingerprint): bool
return false;
}

public function postProcessFingerprintForHash(string $id, string $hash): void
public function postProcessFingerprintForHash(string $id, string $hash, bool $global = false): void
{
$itemKey = $id . self::SUFFIX;
$itemKey = $this->getItemKey($id, $global);

$cacheItem = $this->cache->getItem($itemKey);

Expand All @@ -47,4 +48,17 @@ public function postProcessFingerprintForHash(string $id, string $hash): void
$cacheItem->expiresAt(new \DateTimeImmutable('+1 month'));
$this->cache->save($cacheItem);
}

private function getItemKey(string $id, bool $global = false): string
{
if ($global) {
return $id . self::SUFFIX;
}

return \sprintf(
'%s-%s',
hash('xxh128', PathHelper::getRoot()),
$id,
);
}
}
13 changes: 11 additions & 2 deletions src/Helper/HasherHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ public function writeFile(string $path, FileHashStrategy $strategy = FileHashStr
$path = getcwd() . '/' . $path;
}

$path = realpath($path);

if (false === $path) {
throw new \InvalidArgumentException(\sprintf('The path "%s" is not a valid path.', $path));
}

if (!is_file($path)) {
throw new \InvalidArgumentException(\sprintf('The path "%s" is not a file.', $path));
}
Expand Down Expand Up @@ -79,11 +85,11 @@ public function writeWithFinder(Finder $finder, FileHashStrategy $strategy = Fil
foreach ($finder as $file) {
switch ($strategy) {
case FileHashStrategy::Content:
hash_update_file($this->hashContext, $file->getPathname());
hash_update_file($this->hashContext, $file->getRealPath());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you change that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For coherence with writeFile(), writeGlob() with usage of realpath()

Relative path should not be used with usage of $global


break;
case FileHashStrategy::MTimes:
hash_update($this->hashContext, "{$file->getPathname()}:{$file->getMTime()}");
hash_update($this->hashContext, "{$file->getRealPath()}:{$file->getMTime()}");

break;
}
Expand All @@ -105,6 +111,9 @@ public function writeGlob(string $pattern, FileHashStrategy $strategy = FileHash
throw new \InvalidArgumentException(\sprintf('The pattern "%s" is invalid.', $pattern));
}

$files = array_map('realpath', $files);
$files = array_filter($files);

foreach ($files as $file) {
switch ($strategy) {
case FileHashStrategy::Content:
Expand Down
14 changes: 7 additions & 7 deletions src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -591,34 +591,34 @@ function hasher(string $algo = 'xxh128'): HasherHelper
);
}

function fingerprint_exists(string $id, ?string $fingerprint = null): bool
function fingerprint_exists(string $id, ?string $fingerprint = null, bool $global = false): bool
{
if (null === $fingerprint) {
trigger_deprecation('castor/castor', '0.18.0', 'since 0.18 fingerprint functions require an id argument.');

$fingerprint = $id;
}

return Container::get()->fingerprintHelper->verifyFingerprintFromHash($id, $fingerprint);
return Container::get()->fingerprintHelper->verifyFingerprintFromHash($id, $fingerprint, $global);
}

function fingerprint_save(string $id, ?string $fingerprint = null): void
function fingerprint_save(string $id, ?string $fingerprint = null, bool $global = false): void
{
if (null === $fingerprint) {
trigger_deprecation('castor/castor', '0.18.0', 'since 0.18 fingerprint functions require an id argument.');

$fingerprint = $id;
}

Container::get()->fingerprintHelper->postProcessFingerprintForHash($id, $fingerprint);
Container::get()->fingerprintHelper->postProcessFingerprintForHash($id, $fingerprint, $global);
}

// function fingerprint(callable $callback, string $fingerprint, bool $force = false): bool
/**
* @param string $id
* @param string $fingerprint
*/
function fingerprint(callable $callback, /* string */ $id = null, /* string */ $fingerprint = null, bool $force = false): bool
function fingerprint(callable $callback, /* string */ $id = null, /* string */ $fingerprint = null, bool $force = false, bool $global = false): bool
{
// Could only occur due du BC layer
if (null === $fingerprint && null === $id) {
Expand All @@ -642,9 +642,9 @@ function fingerprint(callable $callback, /* string */ $id = null, /* string */ $
$id = $fingerprint;
}

if ($force || !fingerprint_exists($id, $fingerprint)) {
if ($force || !fingerprint_exists($id, $fingerprint, $global)) {
$callback();
fingerprint_save($id, $fingerprint);
fingerprint_save($id, $fingerprint, $global);

return true;
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace Castor\Tests\Examples\Fingerprint;

class FingerprintTaskWithAGlobalFingerprintTest extends FingerprintedTestCase
{
// fingerprint:task-with-a-fingerprint-global
public function test(): void
{
// Run for the first time, should run
$this->runProcessAndExpect(__FILE__ . '.output_runnable.txt');

// should not run because the fingerprint is the same
$this->runProcessAndExpect(__FILE__ . '.output_not_runnable.txt');

// change file content, should run
$this->runProcessAndExpect(__FILE__ . '.output_runnable.txt', 'Hello World');
}

private function runProcessAndExpect(string $expectedOutputFilePath, string $withFileContent = 'Hello'): void
{
$filepath = \dirname(__DIR__, 3) . '/examples/fingerprint_file.fingerprint_single';
if (file_exists($filepath)) {
unlink($filepath);
}

file_put_contents($filepath, $withFileContent);

$process = $this->runTask(['fingerprint:task-with-a-fingerprint-global']);

if (file_exists($expectedOutputFilePath)) {
$this->assertStringEqualsFile($expectedOutputFilePath, $process->getOutput());
}

$this->assertSame(0, $process->getExitCode());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Hello Task with Global Fingerprint!
Cool! I finished global!
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Hello Task with Global Fingerprint!
Cool, no global fingerprint! Executing...
Cool! I finished global!
1 change: 1 addition & 0 deletions tests/Generated/ListTest.php.output.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ filesystem:filesystem Performs some
filesystem:find Search files and directories on the filesystem
fingerprint:task-with-a-fingerprint Execute a callback only if the fingerprint has changed
fingerprint:task-with-a-fingerprint-and-force Check if the fingerprint has changed before executing a callback (with force option)
fingerprint:task-with-a-fingerprint-global Execute a callback only if the global fingerprint has changed (Shared across all projects)
fingerprint:task-with-complete-fingerprint-check Check if the fingerprint has changed before executing some code
foo:bar Echo foo bar
foo:foo Prints foo
Expand Down