diff --git a/src/Composer/Package/Archiver/ArchiveManager.php b/src/Composer/Package/Archiver/ArchiveManager.php index b127a0d16..6efa47938 100644 --- a/src/Composer/Package/Archiver/ArchiveManager.php +++ b/src/Composer/Package/Archiver/ArchiveManager.php @@ -70,6 +70,54 @@ class ArchiveManager return $this; } + /** + * @return array + * @internal + */ + public function getPackageFilenameParts(CompletePackageInterface $package): array + { + $baseName = $package->getArchiveName(); + if (null === $baseName) { + $baseName = Preg::replace('#[^a-z0-9-_]#i', '-', $package->getName()); + } + + $parts = [ + 'base' => $baseName, + ]; + + $distReference = $package->getDistReference(); + if (null !== $distReference && Preg::isMatch('{^[a-f0-9]{40}$}', $distReference)) { + $parts['dist_reference'] = $distReference; + $parts['dist_type'] = $package->getDistType(); + } else { + $parts['version'] = $package->getPrettyVersion(); + $parts['dist_reference'] = $distReference; + } + + $sourceReference = $package->getSourceReference(); + if (null !== $sourceReference) { + $parts['source_reference'] = substr(sha1($sourceReference), 0, 6); + } + + $parts = array_filter($parts); + foreach ($parts as $key => $part) { + $parts[$key] = str_replace('/', '-', $part); + } + + return $parts; + } + + /** + * @param array $parts + * + * @return string + * @internal + */ + public function getPackageFilenameFromParts(array $parts): string + { + return implode('-', $parts); + } + /** * Generate a distinct filename for a particular version of a package. * @@ -79,28 +127,7 @@ class ArchiveManager */ public function getPackageFilename(CompletePackageInterface $package): string { - if ($package->getArchiveName()) { - $baseName = $package->getArchiveName(); - } else { - $baseName = Preg::replace('#[^a-z0-9-_]#i', '-', $package->getName()); - } - $nameParts = [$baseName]; - - if (null !== $package->getDistReference() && Preg::isMatch('{^[a-f0-9]{40}$}', $package->getDistReference())) { - array_push($nameParts, $package->getDistReference(), $package->getDistType()); - } else { - array_push($nameParts, $package->getPrettyVersion(), $package->getDistReference()); - } - - if ($package->getSourceReference()) { - $nameParts[] = substr(sha1($package->getSourceReference()), 0, 6); - } - - $name = implode('-', array_filter($nameParts, static function ($p): bool { - return !empty($p); - })); - - return str_replace('/', '-', $name); + return $this->getPackageFilenameFromParts($this->getPackageFilenameParts($package)); } /** @@ -169,11 +196,13 @@ class ArchiveManager } } - if (null === $fileName) { - $packageName = $this->getPackageFilename($package); - } else { - $packageName = $fileName; - } + $supportedFormats = $this->getSupportedFormats(); + $packageNameParts = null === $fileName ? + $this->getPackageFilenameParts($package) + : ['base' => $fileName]; + + $packageName = $this->getPackageFilenameFromParts($packageNameParts); + $excludePatterns = $this->buildExcludePatterns($packageNameParts, $supportedFormats); // Archive filename $filesystem->ensureDirectoryExists($targetDir); @@ -188,7 +217,13 @@ class ArchiveManager $tempTarget = sys_get_temp_dir().'/composer_archive'.uniqid().'.'.$format; $filesystem->ensureDirectoryExists(dirname($tempTarget)); - $archivePath = $usableArchiver->archive($sourcePath, $tempTarget, $format, $package->getArchiveExcludes(), $ignoreFilters); + $archivePath = $usableArchiver->archive( + $sourcePath, + $tempTarget, + $format, + array_merge($excludePatterns, $package->getArchiveExcludes()), + $ignoreFilters + ); $filesystem->rename($archivePath, $target); // cleanup temporary download @@ -199,4 +234,54 @@ class ArchiveManager return $target; } + + /** + * @param string[] $parts + * @param string[] $formats + * + * @return string[] + */ + private function buildExcludePatterns(array $parts, array $formats): array + { + $base = $parts['base']; + if (count($parts) > 1) { + $base .= '-*'; + } + + $patterns = []; + foreach ($formats as $format) { + $patterns[] = "$base.$format"; + } + + return $patterns; + } + + /** + * @return string[] + */ + private function getSupportedFormats(): array + { + // The problem is that the \Composer\Package\Archiver\ArchiverInterface + // doesn't provide method to get the supported formats. + // Supported formats are also hard-coded into the description of the + // --format option. + // See \Composer\Command\ArchiveCommand::configure(). + $formats = []; + foreach ($this->archivers as $archiver) { + $items = []; + switch (get_class($archiver)) { + case ZipArchiver::class: + $items = ['zip']; + break; + + case PharArchiver::class: + $items = ['zip', 'tar', 'tar.gz', 'tar.bz2']; + break; + } + + $formats = array_merge($formats, $items); + } + + return array_unique($formats); + } } diff --git a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php index ea66a1f3e..1d4a7d4b5 100644 --- a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php +++ b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php @@ -99,6 +99,30 @@ class ArchiveManagerTest extends ArchiverTest unlink($target); } + public function testGetPackageFilenameParts(): void + { + $expected = [ + 'base' => 'archivertest-archivertest', + 'version' => 'master', + 'source_reference' => '4f26ae', + ]; + $package = $this->setupPackage(); + + self::assertSame( + $expected, + $this->manager->getPackageFilenameParts($package) + ); + } + + public function testGetPackageFilename(): void + { + $package = $this->setupPackage(); + self::assertSame( + 'archivertest-archivertest-master-4f26ae', + $this->manager->getPackageFilename($package) + ); + } + protected function getTargetName(CompletePackage $package, string $format, ?string $fileName = null): string { if (null === $fileName) {