diff --git a/bin/wordpress-packager b/bin/wordpress-packager index da8f8103..3e3ca029 100755 --- a/bin/wordpress-packager +++ b/bin/wordpress-packager @@ -53,7 +53,7 @@ use Symfony\Component\Filesystem\Filesystem; /** @var SourceInterface $source */ $source = new $sourceClass($builder, ReleaseType::from($input->getOption('type'))); $source->fetch(); - if ($input->getOption('unstable')) { + if ($input->getOption('unstable') && method_exists($source, 'fetchUnstable')) { $source->fetchUnstable(); } $packages = $source->get()->getPackages(); diff --git a/src/Package/Package.php b/src/Package/Package.php index cf0d6ada..02341664 100644 --- a/src/Package/Package.php +++ b/src/Package/Package.php @@ -15,6 +15,8 @@ class Package extends CompletePackage implements JsonSerializable { + private \Closure $distCallback; + public function __construct(string $name) { parent::__construct( @@ -22,11 +24,6 @@ public function __construct(string $name) '0.0.0.0', '0.0.0', ); - - $this->withMetadata(); - $this->withLinks(); - $this->withRequires(); - $this->withSuggests(); } /** @@ -50,7 +47,7 @@ public function withVersion(string $version): self return $this; } - protected function withMetadata(): void + protected function withMetadata(): self { $this->setType('wordpress-core'); $this->setDescription('WordPress is web software you can use to create a beautiful website or blog.'); @@ -66,9 +63,11 @@ protected function withMetadata(): void 'cms' ]); $this->setLicense(['GPL-2.0-or-later']); + + return $this; } - protected function withLinks(): void + protected function withLinks(): self { $this->setHomepage('https://wordpress.org/'); $this->setSupport([ @@ -86,9 +85,11 @@ protected function withLinks(): void 'url' => 'https://wordpressfoundation.org/donate/' ] ]); + + return $this; } - public function withRequires(string $minPhpVersion = null): void + public function withRequires(string $minPhpVersion = null): self { if (is_null($minPhpVersion)) { $minPhpVersion = self::getMinPhpVersion($this->getVersion()); @@ -110,9 +111,11 @@ public function withRequires(string $minPhpVersion = null): void '^1.0' ) ]); + + return $this; } - public function withProvides(): void + public function withProvides(): self { if ($this->version === '0.0.0.0') { throw new \Exception('The version must be set before setting implementations provided'); @@ -127,6 +130,8 @@ public function withProvides(): void $this->prettyVersion ) ]); + + return $this; } /** @@ -134,7 +139,7 @@ public function withProvides(): void * * @see https://make.wordpress.org/hosting/handbook/handbook/server-environment/#php-extensions */ - protected function withSuggests(): void + protected function withSuggests(): self { $this->setSuggests([ 'ext-curl' => 'Performs remote request operations.', @@ -152,6 +157,8 @@ protected function withSuggests(): void 'ext-xml' => 'Used for XML parsing, such as from a third-party site.', 'ext-zip' => 'Used for decompressing Plugins, Themes, and WordPress update packages.', ]); + + return $this; } /** @@ -173,11 +180,34 @@ protected static function getMinPhpVersion(string $version): string return '5.6.20'; } + public function setDistCallback(\Closure $callback): void + { + $this->distCallback = $callback; + } + + /** + * Just in time metadata insertion before serialization. + * + * @return void + */ + private function prepareData(): void + { + $this->withMetadata(); + $this->withLinks(); + $this->withSuggests(); + + if (isset($this->distCallback)) { + ($this->distCallback)($this); + } + } + /** * @return array */ public function jsonSerialize(): array { + $this->prepareData(); + $dump = (new ArrayDumper())->dump($this); // TODO: figure out why I have to do this in upstream unset($dump['version_normalized']); diff --git a/src/ReleaseSources/WPDotOrgAPI.php b/src/ReleaseSources/WPDotOrgAPI.php index 4dadd087..265417fd 100644 --- a/src/ReleaseSources/WPDotOrgAPI.php +++ b/src/ReleaseSources/WPDotOrgAPI.php @@ -32,10 +32,16 @@ protected function packageFromObject(stdClass $release): PackageInterface ->withVersion($release->version); $package->setDistType('zip'); - $package->setDistUrl($release->packages->{$this->type->apiName()}); + $package->setDistUrl($distUrl = $release->packages->{$this->type->apiName()}); $package->withRequires($release->php_version); $package->withProvides(); + $package->setDistCallback(function ($pack) use ($distUrl): void { + if ($sha1 = $this->getDistSha1Checksum($distUrl)) { + $pack->setDistSha1Checksum($sha1); + } + }); + return $package; } @@ -44,6 +50,11 @@ public function fetchUnstable(string $endpoint = null): void $this->fetch(($endpoint ?? $this::ENDPOINT) . '?channel=beta'); } + protected function getDistSha1Checksum(string $distUrl): string + { + return trim(file_get_contents($distUrl . '.sha1') ?: ''); + } + public function fetch(string $endpoint = null): self { $this->data = array_merge( diff --git a/tests/Package/PackageTest.php b/tests/Package/PackageTest.php index 032d3c8d..086f3337 100644 --- a/tests/Package/PackageTest.php +++ b/tests/Package/PackageTest.php @@ -71,7 +71,7 @@ public function testJsonSerialize() $json = new JsonFile($this->jsonFile); $this->assertTrue($json->validateSchema()); - $this->assertJsonFileEqualsJsonFile($this->jsonFile, __DIR__ . '/../resources/composer.json'); + $this->assertJsonFileEqualsJsonFile($this->jsonFile, __DIR__ . '/../resources/package-composer.json'); } public function testGreaterThanOrEqualTo() @@ -95,16 +95,18 @@ public function testMinPhpVersion() $builder = new Package('roots/wordpress'); - $phpNew1 = $getPhpPackage($builder->clone()->withVersion('5.2.1')); - $phpNew2 = $getPhpPackage($builder->clone()->withVersion('5.2')); - $phpNew3 = $getPhpPackage($builder->clone()->withVersion('5.2-beta1')); - $phpOld1 = $getPhpPackage($builder->clone()->withVersion('5.1')); - $phpOld2 = $getPhpPackage($builder->clone()->withVersion('4.0')); - - $this->assertTrue($phpNew1->matches(new Constraint('=', '5.6.20'))); - $this->assertTrue($phpNew2->matches(new Constraint('=', '5.6.20'))); - $this->assertTrue($phpNew3->matches(new Constraint('=', '5.6.20'))); - $this->assertTrue($phpOld1->matches(new Constraint('=', '5.2.4'))); - $this->assertTrue($phpOld2->matches(new Constraint('=', '5.2.4'))); + $phpNew1 = $getPhpPackage($builder->clone()->withVersion('5.2.1')->withRequires()); + $phpNew2 = $getPhpPackage($builder->clone()->withVersion('5.2')->withRequires()); + $phpNew3 = $getPhpPackage($builder->clone()->withVersion('5.2-beta1')->withRequires()); + $phpOld1 = $getPhpPackage($builder->clone()->withVersion('5.1')->withRequires()); + $phpOld2 = $getPhpPackage($builder->clone()->withVersion('4.0')->withRequires()); + $phpCustom = $getPhpPackage($builder->clone()->withVersion('7.8')->withRequires('7.7.7')); + + $this->assertTrue($phpNew1->matches(new Constraint(Constraint::STR_OP_EQ, '5.6.20'))); + $this->assertTrue($phpNew2->matches(new Constraint(Constraint::STR_OP_EQ, '5.6.20'))); + $this->assertTrue($phpNew3->matches(new Constraint(Constraint::STR_OP_EQ, '5.6.20'))); + $this->assertTrue($phpOld1->matches(new Constraint(Constraint::STR_OP_EQ, '5.2.4'))); + $this->assertTrue($phpOld2->matches(new Constraint(Constraint::STR_OP_EQ, '5.2.4'))); + $this->assertTrue($phpCustom->matches(new Constraint(Constraint::STR_OP_EQ, '7.7.7'))); } } diff --git a/tests/ReleaseSources/WPDotOrgAPITest.php b/tests/ReleaseSources/WPDotOrgAPITest.php index 049e6d3b..82cee28c 100644 --- a/tests/ReleaseSources/WPDotOrgAPITest.php +++ b/tests/ReleaseSources/WPDotOrgAPITest.php @@ -20,11 +20,17 @@ public function testGet(): void $this->assertInstanceOf(Repository::class, $repo); $this->assertNotEmpty($repo); + /** @var Package $wpLatest */ $wpLatest = $repo->findPackage($pkgName, '5.8.3'); $this->assertEquals( '5.8.3', $wpLatest->getPrettyVersion() ); + + $this->assertJsonStringEqualsJsonFile( + __DIR__ . '/../resources/source-composer.json', + json_encode($wpLatest, JSON_UNESCAPED_SLASHES) + ); } public function testGetWithUnstable(): void diff --git a/tests/resources/composer.json b/tests/resources/package-composer.json similarity index 100% rename from tests/resources/composer.json rename to tests/resources/package-composer.json diff --git a/tests/resources/source-composer.json b/tests/resources/source-composer.json new file mode 100644 index 00000000..65206fc2 --- /dev/null +++ b/tests/resources/source-composer.json @@ -0,0 +1,64 @@ +{ + "name": "roots/wordpress-dotorg", + "version": "5.8.3", + "dist": { + "type": "zip", + "url": "https://downloads.wordpress.org/release/wordpress-5.8.3.zip", + "shasum": "27f45019dc89125bf3a87f09c354ae54ea2ea4c9" + }, + "provide": { + "wordpress/core-implementation": "5.8.3" + }, + "require": { + "php": ">= 5.6.20", + "roots/wordpress-core-installer": "^1.0" + }, + "suggest": { + "ext-curl": "Performs remote request operations.", + "ext-dom": "Used to validate Text Widget content and to automatically configuring IIS7+.", + "ext-exif": "Works with metadata stored in images.", + "ext-fileinfo": "Used to detect mimetype of file uploads.", + "ext-hash": "Used for hashing, including passwords and update packages.", + "ext-imagick": "Provides better image quality for media uploads.", + "ext-json": "Used for communications with other servers.", + "ext-libsodium": "Validates Signatures and provides securely random bytes.", + "ext-mbstring": "Used to properly handle UTF8 text.", + "ext-mysqli": "Connects to MySQL for database interactions.", + "ext-openssl": "Permits SSL-based connections to other hosts.", + "ext-pcre": "Increases performance of pattern matching in code searches.", + "ext-xml": "Used for XML parsing, such as from a third-party site.", + "ext-zip": "Used for decompressing Plugins, Themes, and WordPress update packages." + }, + "type": "wordpress-core", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "WordPress Community", + "homepage": "https://wordpress.org/about/" + } + ], + "description": "WordPress is web software you can use to create a beautiful website or blog.", + "homepage": "https://wordpress.org/", + "keywords": [ + "blog", + "cms", + "wordpress" + ], + "support": { + "issues": "https://core.trac.wordpress.org/", + "forum": "https://wordpress.org/support/", + "wiki": "https://codex.wordpress.org/", + "irc": "irc://irc.freenode.net/wordpress", + "source": "https://core.trac.wordpress.org/browser", + "docs": "https://developer.wordpress.org/", + "rss": "https://wordpress.org/news/feed/" + }, + "funding": [ + { + "type": "other", + "url": "https://wordpressfoundation.org/donate/" + } + ] +}