Skip to content

Commit 5c0c261

Browse files
committed
Use local filesystem interface for services
1 parent 6f2d032 commit 5c0c261

14 files changed

Lines changed: 129 additions & 35 deletions

File tree

src/Agent/Skills/SkillsSynchronizer.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020
namespace FastForward\DevTools\Agent\Skills;
2121

2222
use FastForward\DevTools\Filesystem\FinderFactoryInterface;
23+
use FastForward\DevTools\Filesystem\FilesystemInterface;
2324
use Psr\Log\LoggerAwareInterface;
2425
use Psr\Log\LoggerInterface;
25-
use Symfony\Component\Filesystem\Filesystem;
2626
use Symfony\Component\Filesystem\Path;
2727

2828
/**
@@ -37,12 +37,12 @@ final class SkillsSynchronizer implements LoggerAwareInterface
3737
/**
3838
* Initializes the synchronizer with a filesystem and finder factory.
3939
*
40-
* @param Filesystem $filesystem Filesystem instance for file operations
40+
* @param FilesystemInterface $filesystem Filesystem instance for file operations
4141
* @param FinderFactoryInterface $finderFactory Factory for locating skill directories in the package
4242
* @param LoggerInterface $logger Logger for recording synchronization actions and decisions
4343
*/
4444
public function __construct(
45-
private readonly Filesystem $filesystem,
45+
private readonly FilesystemInterface $filesystem,
4646
private readonly FinderFactoryInterface $finderFactory,
4747
private LoggerInterface $logger,
4848
) {}

src/Console/Command/GitAttributesCommand.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919

2020
namespace FastForward\DevTools\Console\Command;
2121

22+
use Composer\Command\BaseCommand;
2223
use FastForward\DevTools\Composer\Json\ComposerJsonInterface;
2324
use FastForward\DevTools\Filesystem\FilesystemInterface;
24-
use Composer\Command\BaseCommand;
2525
use FastForward\DevTools\GitAttributes\CandidateProviderInterface;
2626
use FastForward\DevTools\GitAttributes\ExistenceCheckerInterface;
2727
use FastForward\DevTools\GitAttributes\ExportIgnoreFilterInterface;
@@ -31,7 +31,6 @@
3131
use Symfony\Component\Console\Attribute\AsCommand;
3232
use Symfony\Component\Console\Input\InputInterface;
3333
use Symfony\Component\Console\Output\OutputInterface;
34-
use Symfony\Component\Filesystem\Filesystem;
3534

3635
use function Safe\getcwd;
3736

@@ -66,7 +65,7 @@ final class GitAttributesCommand extends BaseCommand
6665
* @param MergerInterface $merger the merger component
6766
* @param ReaderInterface $reader the reader component
6867
* @param WriterInterface $writer the writer component
69-
* @param Filesystem $filesystem the filesystem component
68+
* @param FilesystemInterface $filesystem the filesystem component
7069
* @param ComposerJsonInterface $composer the composer.json accessor
7170
*/
7271
public function __construct(

src/Filesystem/Filesystem.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,44 @@ public function chmod(string|iterable $files, int $mode, int $umask = 0o000, boo
102102
parent::chmod($this->getAbsolutePath($files), $mode, $umask, $recursive);
103103
}
104104

105+
/**
106+
* Removes files, symbolic links, or directories.
107+
*
108+
* @param iterable<string>|string $files the file(s), link(s), or directory(ies) to remove
109+
*/
110+
#[Override]
111+
public function remove(string|iterable $files): void
112+
{
113+
parent::remove($this->getAbsolutePath($files));
114+
}
115+
116+
/**
117+
* Creates a symbolic link.
118+
*
119+
* @param string $originDir the origin path the link MUST point to
120+
* @param string $targetDir the link path to create
121+
* @param bool $copyOnWindows whether directories SHOULD be copied on Windows instead of linked
122+
*/
123+
#[Override]
124+
public function symlink(string $originDir, string $targetDir, bool $copyOnWindows = false): void
125+
{
126+
parent::symlink($this->getAbsolutePath($originDir), $this->getAbsolutePath($targetDir), $copyOnWindows);
127+
}
128+
129+
/**
130+
* Reads a symbolic link target.
131+
*
132+
* @param string $path the symbolic link path
133+
* @param bool $canonicalize whether the returned path SHOULD be canonicalized
134+
*
135+
* @return string|null the link target, or null when the path is not a symbolic link
136+
*/
137+
#[Override]
138+
public function readlink(string $path, bool $canonicalize = false): ?string
139+
{
140+
return parent::readlink($this->getAbsolutePath($path), $canonicalize);
141+
}
142+
105143
/**
106144
* Resolves a path or iterable of paths into their absolute path representation.
107145
*

src/Filesystem/FilesystemInterface.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,32 @@ public function copy(string $originFile, string $targetFile, bool $overwriteNewe
7575
*/
7676
public function chmod(string|iterable $files, int $mode, int $umask = 0o000, bool $recursive = false): void;
7777

78+
/**
79+
* Removes files, symbolic links, or directories.
80+
*
81+
* @param iterable<string>|string $files the file(s), link(s), or directory(ies) to remove
82+
*/
83+
public function remove(string|iterable $files): void;
84+
85+
/**
86+
* Creates a symbolic link.
87+
*
88+
* @param string $originDir the origin path the link MUST point to
89+
* @param string $targetDir the link path to create
90+
* @param bool $copyOnWindows whether directories SHOULD be copied on Windows instead of linked
91+
*/
92+
public function symlink(string $originDir, string $targetDir, bool $copyOnWindows = false): void;
93+
94+
/**
95+
* Reads a symbolic link target.
96+
*
97+
* @param string $path the symbolic link path
98+
* @param bool $canonicalize whether the returned path SHOULD be canonicalized
99+
*
100+
* @return string|null the link target, or null when the path is not a symbolic link
101+
*/
102+
public function readlink(string $path, bool $canonicalize = false): ?string;
103+
78104
/**
79105
* Resolves a path or iterable of paths into their absolute path representation.
80106
*

src/GitAttributes/ExistenceChecker.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919

2020
namespace FastForward\DevTools\GitAttributes;
2121

22-
use Symfony\Component\Filesystem\Filesystem;
22+
use FastForward\DevTools\Filesystem\Filesystem;
23+
use FastForward\DevTools\Filesystem\FilesystemInterface;
2324

2425
/**
2526
* Checks the existence of files and directories in a given base path.
@@ -30,10 +31,10 @@
3031
final readonly class ExistenceChecker implements ExistenceCheckerInterface
3132
{
3233
/**
33-
* @param Filesystem $filesystem
34+
* @param FilesystemInterface $filesystem
3435
*/
3536
public function __construct(
36-
private Filesystem $filesystem = new Filesystem()
37+
private FilesystemInterface $filesystem = new Filesystem()
3738
) {}
3839

3940
/**

src/GitAttributes/Writer.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
namespace FastForward\DevTools\GitAttributes;
2121

22-
use Symfony\Component\Filesystem\Filesystem;
22+
use FastForward\DevTools\Filesystem\FilesystemInterface;
2323

2424
use function Safe\preg_split;
2525

@@ -33,10 +33,10 @@
3333
final readonly class Writer implements WriterInterface
3434
{
3535
/**
36-
* @param Filesystem $filesystem the filesystem service responsible for writing the file
36+
* @param FilesystemInterface $filesystem the filesystem service responsible for writing the file
3737
*/
3838
public function __construct(
39-
private Filesystem $filesystem
39+
private FilesystemInterface $filesystem
4040
) {}
4141

4242
/**

src/GitIgnore/Writer.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
namespace FastForward\DevTools\GitIgnore;
2121

22-
use Symfony\Component\Filesystem\Filesystem;
22+
use FastForward\DevTools\Filesystem\FilesystemInterface;
2323

2424
/**
2525
* Renders and persists normalized .gitignore content.
@@ -37,11 +37,11 @@
3737
* The provided filesystem implementation MUST support writing file contents
3838
* to the target path returned by a GitIgnoreInterface instance.
3939
*
40-
* @param Filesystem $filesystem The filesystem service responsible for
41-
* writing the rendered .gitignore content.
40+
* @param FilesystemInterface $filesystem The filesystem service responsible for
41+
* writing the rendered .gitignore content.
4242
*/
4343
public function __construct(
44-
private Filesystem $filesystem
44+
private FilesystemInterface $filesystem
4545
) {}
4646

4747
/**

src/License/Generator.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121

2222
use Throwable;
2323
use FastForward\DevTools\Composer\Json\ComposerJsonInterface;
24+
use FastForward\DevTools\Filesystem\FilesystemInterface;
2425
use Psr\Clock\ClockInterface;
25-
use Symfony\Component\Filesystem\Filesystem;
2626
use Twig\Environment;
2727

2828
/**
@@ -44,7 +44,7 @@
4444
*
4545
* @param ResolverInterface $resolver The resolver for mapping license identifiers to templates
4646
* @param ComposerJsonInterface $composer
47-
* @param Filesystem $filesystem The filesystem component for file operations
47+
* @param FilesystemInterface $filesystem The filesystem component for file operations
4848
* @param ClockInterface $clock
4949
* @param Environment $renderer
5050
*/
@@ -53,7 +53,7 @@ public function __construct(
5353
private ComposerJsonInterface $composer,
5454
private ClockInterface $clock,
5555
private Environment $renderer,
56-
private Filesystem $filesystem,
56+
private FilesystemInterface $filesystem,
5757
) {}
5858

5959
/**

tests/Agent/Skills/SkillsSynchronizerTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@
2323
use FastForward\DevTools\Agent\Skills\SkillsSynchronizer;
2424
use FastForward\DevTools\Agent\Skills\SynchronizeResult;
2525
use FastForward\DevTools\Filesystem\FinderFactoryInterface;
26+
use FastForward\DevTools\Filesystem\FilesystemInterface;
2627
use PHPUnit\Framework\Attributes\CoversClass;
2728
use PHPUnit\Framework\Attributes\Test;
2829
use PHPUnit\Framework\Attributes\UsesClass;
2930
use PHPUnit\Framework\TestCase;
3031
use Prophecy\PhpUnit\ProphecyTrait;
3132
use Prophecy\Prophecy\ObjectProphecy;
3233
use Psr\Log\LoggerInterface;
33-
use Symfony\Component\Filesystem\Filesystem;
3434
use Symfony\Component\Finder\Finder;
3535
use Symfony\Component\Finder\SplFileInfo;
3636

@@ -45,7 +45,7 @@ final class SkillsSynchronizerTest extends TestCase
4545
private const string CONSUMER_SKILLS_PATH = '/consumer/.agents/skills';
4646

4747
/**
48-
* @var ObjectProphecy<Filesystem>
48+
* @var ObjectProphecy<FilesystemInterface>
4949
*/
5050
private ObjectProphecy $filesystem;
5151

@@ -69,7 +69,7 @@ final class SkillsSynchronizerTest extends TestCase
6969
*/
7070
protected function setUp(): void
7171
{
72-
$this->filesystem = $this->prophesize(Filesystem::class);
72+
$this->filesystem = $this->prophesize(FilesystemInterface::class);
7373
$this->finderFactory = $this->prophesize(FinderFactoryInterface::class);
7474
$this->finder = $this->prophesize(Finder::class);
7575
$this->logger = $this->prophesize(LoggerInterface::class);

tests/Filesystem/FilesystemTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
use PHPUnit\Framework\TestCase;
2626
use Symfony\Component\Filesystem\Path;
2727

28+
use function Safe\file_put_contents;
2829
use function Safe\getcwd;
2930

3031
#[CoversClass(Filesystem::class)]
@@ -152,4 +153,33 @@ public function mkdirWillCreateDirectoryWithRelativePath(): void
152153
self::assertTrue($this->filesystem->exists($dirName, $this->tempDir));
153154
self::assertDirectoryExists($this->tempDir . '/' . $dirName);
154155
}
156+
157+
/**
158+
* @return void
159+
*/
160+
#[Test]
161+
public function removeWillDeleteRelativePathAgainstCurrentWorkingDirectory(): void
162+
{
163+
$filename = $this->tempDir . '/remove-me.txt';
164+
file_put_contents($filename, 'temporary');
165+
166+
$this->filesystem->remove($filename);
167+
168+
self::assertFileDoesNotExist($filename);
169+
}
170+
171+
/**
172+
* @return void
173+
*/
174+
#[Test]
175+
public function symlinkAndReadlinkWillUseAbsolutePaths(): void
176+
{
177+
$origin = $this->tempDir . '/origin';
178+
$target = $this->tempDir . '/target';
179+
180+
$this->filesystem->mkdir($origin);
181+
$this->filesystem->symlink($origin, $target);
182+
183+
self::assertSame($origin, $this->filesystem->readlink($target, true));
184+
}
155185
}

0 commit comments

Comments
 (0)