Skip to content
Open
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
203 changes: 143 additions & 60 deletions src/Core/Console/VendorPublishCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,21 @@
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Arr;
use Illuminate\Support\ServiceProvider;
use League\Flysystem\Adapter\Local;
use Illuminate\Support\Str;
use League\Flysystem\Filesystem as Flysystem;
use League\Flysystem\Local\LocalFilesystemAdapter as LocalAdapter;
use League\Flysystem\MountManager;
use League\Flysystem\UnixVisibility\PortableVisibilityConverter;
use League\Flysystem\Visibility;
use Symfony\Component\Console\Attribute\AsCommand;

#[AsCommand(name: 'vendor:publish')]
class VendorPublishCommand extends Command
{
/**
* @var Filesystem
* The filesystem instance.
*
* @var \Illuminate\Filesystem\Filesystem
*/
protected $files;

Expand All @@ -21,28 +29,51 @@ class VendorPublishCommand extends Command
*
* @var string
*/
protected $provider;
protected ?string $provider = null;

/**
* The tags to publish.
*
* @var array
*/
protected $tags = [];
protected array $tags = [];

/**
* The console command signature.
*
* @var string
*/
protected $signature = 'vendor:publish {--force : Overwrite any existing files.}
{--all : Publish assets for all service providers without prompt.}
{--provider= : The service provider that has assets you want to publish.}
{--tag=* : One or many tags that have assets you want to publish.}';
protected $signature = 'vendor:publish
{--existing : Publish and overwrite only the files that have already been published}
{--force : Overwrite any existing files}
{--all : Publish assets for all service providers without prompt}
{--provider= : The service provider that has assets you want to publish}
{--tag=* : One or many tags that have assets you want to publish}';

/**
* The name of the console command.
*
* This name is used to identify the command during lazy loading.
*
* @var string|null
*
* @deprecated
*/
protected static $defaultName = 'vendor:publish';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Publish any publishable assets from vendor packages';

/**
* Create a new command instance.
*
* @param \Illuminate\Filesystem\Filesystem $files
* @return void
*/
public function __construct(Filesystem $files)
{
parent::__construct();
Expand All @@ -52,30 +83,31 @@ public function __construct(Filesystem $files)

/**
* Execute the console command.
*
* @return void
*/
public function handle()
public function handle(): void
{
$this->determineWhatShouldBePublished();

foreach ($this->tags ?: [null] as $tag) {
foreach ($this->tags as $tag) {
$this->publishTag($tag);
}

$this->info('Publishing complete.');
}

/**
* Determine the provider of tag(s) to publish.
* Determine the provider or tag(s) to publish.
*
* @return void
*/
protected function determineWhatShouldBePublished()
protected function determineWhatShouldBePublished(): void
{
if ($this->option('all')) {
return;
}

list($this->provider, $this->tags) = [
$this->option('provider'),
(array) $this->option('tag'),
[$this->provider, $this->tags] = [
$this->option('provider'), (array) $this->option('tag'),
];

if (! $this->provider && ! $this->tags) {
Expand All @@ -85,12 +117,14 @@ protected function determineWhatShouldBePublished()

/**
* Prompt for which provider or tag to publish.
*
* @return void
*/
protected function promptForProviderOrTag()
protected function promptForProviderOrTag(): void
{
$choice = $this->choice(
$choice = $this->components->choice(
"Which provider or tag's files would you like to publish?",
$choices = $this->publishableChoices(),
$choices = $this->publishableChoices()
);

if ($choice == $choices[0] || is_null($choice)) {
Expand All @@ -101,27 +135,28 @@ protected function promptForProviderOrTag()
}

/**
* return the choices available via the prompt.
* The choices available via the prompt.
*
* @return array
*/
protected function publishableChoices()
protected function publishableChoices(): array
{
return array_merge(
['<comment>Publish files from all providers and tags listed below</comment>'],
preg_filter('/^/', '<comment>Provider: </comment>', Arr::sort(ServiceProvider::publishableProviders())),
preg_filter('/^/', '<comment>Tag: </comment>', Arr::sort(ServiceProvider::publishableGroups())),
preg_filter('/^/', '<fg=gray>Provider:</> ', Arr::sort(ServiceProvider::publishableProviders())),
preg_filter('/^/', '<fg=gray>Tag:</> ', Arr::sort(ServiceProvider::publishableGroups()))
);
}

/**
* Parse the answer that was given via the prompt.
*
* @param string $choice
* @return void
*/
protected function parseChoice($choice)
protected function parseChoice(string $choice): void
{
list($type, $value) = explode(': ', strip_tags($choice));
[$type, $value] = explode(': ', strip_tags($choice));

if ($type === 'Provider') {
$this->provider = $value;
Expand All @@ -131,95 +166,137 @@ protected function parseChoice($choice)
}

/**
* Publish the assets for a tag.
* Publishes the assets for a tag.
*
* @param string $tag
* @return void
*/
protected function publishTag($tag)
protected function publishTag(string $tag): void
{
foreach ($this->pathsToPublish($tag) as $from => $to) {
$published = false;

$pathsToPublish = $this->pathsToPublish($tag);

if ($publishing = count($pathsToPublish) > 0) {
$this->components->info(sprintf(
'Publishing %sassets',
$tag ? "[$tag] " : '',
));
}

foreach ($pathsToPublish as $from => $to) {
$this->publishItem($from, $to);
}

if ($publishing === false) {
$this->components->info('No publishable resources for tag ['.$tag.'].');
} else {
$this->newLine();
}
}

/**
* Get all the paths to publish.
* Get all of the paths to publish.
*
* @param string $tag
*
* @return array
*/
protected function pathsToPublish($tag)
protected function pathsToPublish(string $tag): array
{
return ServiceProvider::pathsToPublish($this->provider, $tag);
return ServiceProvider::pathsToPublish(
$this->provider, $tag
);
}

/**
* Publish the given item from and to the given location.
*
* @param string $from
* @param string $to
* @return void
*/
protected function publishItem($from, $to)
protected function publishItem(string $from, string $to): void
{
if ($this->files->isFile($from)) {
$this->publishFile($from, $to);

return;
} elseif ($this->files->isDirectory($from)) {
$this->publishDirectory($from, $to);

return;
}

$this->error("Can't locate path: <{$from}>");
$this->components->error("Can't locate path: <{$from}>");
}

/**
* Publish the file to the given path.
*
* @param string $from
* @param string $to
* @return void
*/
protected function publishFile($from, $to)
protected function publishFile(string $from, string $to): void
{
if (! $this->files->exists($to) || $this->option('force')) {
if ((! $this->option('existing') && (! $this->files->exists($to) || $this->option('force')))
|| ($this->option('existing') && $this->files->exists($to))) {
$this->createParentDirectory(dirname($to));

$this->files->copy($from, $to);
$this->status($from, $to, 'File');

$this->status($from, $to, 'file');
} else {
if ($this->option('existing')) {
$this->components->twoColumnDetail(sprintf(
'File [%s] does not exist',
str_replace(base_path().'/', '', $to),
), '<fg=yellow;options=bold>SKIPPED</>');
} else {
$this->components->twoColumnDetail(sprintf(
'File [%s] already exists',
str_replace(base_path().'/', '', realpath($to)),
), '<fg=yellow;options=bold>SKIPPED</>');
}
}
}

/**
* Publish the directory to the given directory.
*
* @param string $from
* @param string $to
*
* @throws \League\Flysystem\FileNotFoundException
* @param string $from
* @param string $to
* @return void
*/
protected function publishDirectory($from, $to)
protected function publishDirectory($from, $to): void
{
$visibility = PortableVisibilityConverter::fromArray([], Visibility::PUBLIC);

$this->moveManagedFiles(new MountManager([
'from' => new \League\Flysystem\Filesystem(new Local($from)),
'to' => new \League\Flysystem\Filesystem(new Local($to)),
'from' => new Flysystem(new LocalAdapter($from)),
'to' => new Flysystem(new LocalAdapter($to, $visibility)),
]));

$this->status($from, $to, 'Directory');
$this->status($from, $to, 'directory');
}

/**
* Move all the files in the given MountManager.
*
* @param \League\Flysystem\MountManager $manager
*
* @throws \League\Flysystem\FileNotFoundException
* @return void
*/
protected function moveManagedFiles($manager)
protected function moveManagedFiles(MountManager $manager): void
{
foreach ($manager->listContents('from://', true) as $file) {
if ($file['type'] === 'file' && (! $manager->has('to://' . $file['path']) || $this->option('force'))) {
$manager->put('to://' . $file['path'], $manager->read('from://' . $file['path']));
$path = Str::after($file['path'], 'from://');

if (
$file['type'] === 'file'
&& (
(! $this->option('existing') && (! $manager->fileExists('to://'.$path) || $this->option('force')))
|| ($this->option('existing') && $manager->fileExists('to://'.$path))
)
) {
$manager->write('to://'.$path, $manager->read($file['path']));
}
}
}
Expand All @@ -228,8 +305,9 @@ protected function moveManagedFiles($manager)
* Create the directory to house the published files if needed.
*
* @param string $directory
* @return void
*/
protected function createParentDirectory($directory)
protected function createParentDirectory(string $directory): void
{
if (! $this->files->isDirectory($directory)) {
$this->files->makeDirectory($directory, 0755, true);
Expand All @@ -242,14 +320,19 @@ protected function createParentDirectory($directory)
* @param string $from
* @param string $to
* @param string $type
* @return void
*/
protected function status($from, $to, $type)
protected function status(string $from, string $to, string $type): void
{
$from = str_replace(base_path(), '', realpath($from));
$to = str_replace(base_path(), '', realpath($to));
$from = str_replace(base_path().'/', '', realpath($from));

$this->line(
"<info>Copied {$type}</info> <comment>[{$from}]</comment> <info>To</info> <comment>[{$to}]</comment>",
);
$to = str_replace(base_path().'/', '', realpath($to));

$this->components->task(sprintf(
'Copying %s [%s] to [%s]',
$type,
$from,
$to,
));
}
}