From a0550279c78e36e51e9753c3a186e7c4c4333b24 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Thu, 18 Jul 2024 01:01:46 +0400 Subject: [PATCH] Add GithubRepository implementation --- composer.json | 1 + composer.lock | 862 +++++++++++------- src/Module/Environment/Architecture.php | 27 +- src/Module/Environment/OperatingSystem.php | 20 +- src/Module/Repository/Internal/Asset.php | 8 +- .../Repository/Internal/AssetsCollection.php | 2 + .../Internal/GitHub/GitHubAsset.php | 80 ++ .../Internal/GitHub/GitHubRelease.php | 106 +++ .../Internal/GitHub/GitHubRepository.php | 150 +++ src/Module/Repository/Internal/Release.php | 7 +- .../Internal/ReleasesCollection.php | 2 + .../Internal/RepositoriesCollection.php | 4 + 12 files changed, 911 insertions(+), 358 deletions(-) create mode 100644 src/Module/Repository/Internal/GitHub/GitHubAsset.php create mode 100644 src/Module/Repository/Internal/GitHub/GitHubRelease.php create mode 100644 src/Module/Repository/Internal/GitHub/GitHubRepository.php diff --git a/composer.json b/composer.json index af30940..ecc9e53 100644 --- a/composer.json +++ b/composer.json @@ -20,6 +20,7 @@ ], "require": { "php": ">=8.1", + "symfony/http-client": "^4.4.11 || ^5.0 || ^6.0 || ^7.0", "yiisoft/injector": "^1.2" }, "require-dev": { diff --git a/composer.lock b/composer.lock index 1802b56..a187d2e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,503 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d9955f7a2ec56a6b172516321ba6982d", - "packages": [], + "content-hash": "2a399b2da365c5e93f15813f1b60f221", + "packages": [ + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/log", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, + "time": "2021-07-14T16:46:02+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/http-client", + "version": "v6.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client.git", + "reference": "6e9db0025db565bcf8f1d46ed734b549e51e6045" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client/zipball/6e9db0025db565bcf8f1d46ed734b549e51e6045", + "reference": "6e9db0025db565bcf8f1d46ed734b549e51e6045", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-client-contracts": "^3.4.1", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "php-http/discovery": "<1.15", + "symfony/http-foundation": "<6.3" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "1.0", + "symfony/http-client-implementation": "3.0" + }, + "require-dev": { + "amphp/amp": "^2.5", + "amphp/http-client": "^4.2.1", + "amphp/http-tunnel": "^1.0", + "amphp/socket": "^1.1", + "guzzlehttp/promises": "^1.4|^2.0", + "nyholm/psr7": "^1.0", + "php-http/httplug": "^1.0|^2.0", + "psr/http-client": "^1.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", + "homepage": "https://symfony.com", + "keywords": [ + "http" + ], + "support": { + "source": "https://github.com/symfony/http-client/tree/v6.4.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-06-28T07:59:05+00:00" + }, + { + "name": "symfony/http-client-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client-contracts.git", + "reference": "20414d96f391677bf80078aa55baece78b82647d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/20414d96f391677bf80078aa55baece78b82647d", + "reference": "20414d96f391677bf80078aa55baece78b82647d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to HTTP clients", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, + { + "name": "yiisoft/injector", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/yiisoft/injector.git", + "reference": "0dc0127a7542341bdaabda7b85204e992938b83e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yiisoft/injector/zipball/0dc0127a7542341bdaabda7b85204e992938b83e", + "reference": "0dc0127a7542341bdaabda7b85204e992938b83e", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "require-dev": { + "maglnet/composer-require-checker": "^3.8|^4.2", + "phpbench/phpbench": "^1.1", + "phpunit/phpunit": "^9.5", + "psr/container": "^1.0|^2.0", + "rector/rector": "^0.18.12", + "roave/infection-static-analysis-plugin": "^1.16", + "spatie/phpunit-watcher": "^1.23", + "vimeo/psalm": "^4.30|^5.7", + "yiisoft/test-support": "^1.2" + }, + "suggest": { + "psr/container": "For automatic resolving of dependencies" + }, + "type": "library", + "autoload": { + "psr-4": { + "Yiisoft\\Injector\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "PSR-11 compatible injector. Executes a callable and makes an instances by injecting dependencies from a given DI container.", + "homepage": "https://www.yiiframework.com/", + "keywords": [ + "PSR-11", + "dependency injection", + "di", + "injector", + "reflection" + ], + "support": { + "chat": "https://t.me/yii3en", + "forum": "https://www.yiiframework.com/forum/", + "irc": "irc://irc.freenode.net/yii", + "issues": "https://github.com/yiisoft/injector/issues?state=open", + "source": "https://github.com/yiisoft/injector", + "wiki": "https://www.yiiframework.com/wiki/" + }, + "funding": [ + { + "url": "https://github.com/yiisoft", + "type": "github" + }, + { + "url": "https://opencollective.com/yiisoft", + "type": "open_collective" + } + ], + "time": "2023-12-20T09:39:03+00:00" + } + ], "packages-dev": [ { "name": "amphp/amp", @@ -2868,59 +3363,6 @@ ], "time": "2024-04-05T04:39:01+00:00" }, - { - "name": "psr/container", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "shasum": "" - }, - "require": { - "php": ">=7.4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/2.0.2" - }, - "time": "2021-11-05T16:47:00+00:00" - }, { "name": "psr/event-dispatcher", "version": "1.0.0", @@ -3022,89 +3464,36 @@ "response" ], "support": { - "source": "https://github.com/php-fig/http-factory" - }, - "time": "2024-04-15T12:06:14+00:00" - }, - { - "name": "psr/http-message", - "version": "2.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Http\\Message\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", - "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" - ], - "support": { - "source": "https://github.com/php-fig/http-message/tree/2.0" + "source": "https://github.com/php-fig/http-factory" }, - "time": "2023-04-04T09:54:51+00:00" + "time": "2024-04-15T12:06:14+00:00" }, { - "name": "psr/log", - "version": "3.0.0", + "name": "psr/http-message", + "version": "2.0", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", "shasum": "" }, "require": { - "php": ">=8.0.0" + "php": "^7.2 || ^8.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "src" + "Psr\\Http\\Message\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3117,17 +3506,20 @@ "homepage": "https://www.php-fig.org/" } ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", "keywords": [ - "log", + "http", + "http-message", "psr", - "psr-3" + "psr-7", + "request", + "response" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" + "source": "https://github.com/php-fig/http-message/tree/2.0" }, - "time": "2021-07-14T16:46:02+00:00" + "time": "2023-04-04T09:54:51+00:00" }, { "name": "react/cache", @@ -4737,73 +5129,6 @@ ], "time": "2024-06-28T09:49:33+00:00" }, - { - "name": "symfony/deprecation-contracts", - "version": "v3.5.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", - "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "files": [ - "function.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "A generic function and convention to trigger deprecation notices", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-04-18T09:32:20+00:00" - }, { "name": "symfony/event-dispatcher", "version": "v6.4.8", @@ -5692,89 +6017,6 @@ ], "time": "2024-05-31T14:49:08+00:00" }, - { - "name": "symfony/service-contracts", - "version": "v3.5.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", - "reference": "bd1d9e59a81d8fa4acdcea3f617c581f7475a80f", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "psr/container": "^1.1|^2.0", - "symfony/deprecation-contracts": "^2.5|^3" - }, - "conflict": { - "ext-psr": "<1.1|>=2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.5-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Service\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.5.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-04-18T09:32:20+00:00" - }, { "name": "symfony/stopwatch", "version": "v6.4.8", @@ -6364,76 +6606,6 @@ "source": "https://github.com/webmozarts/assert/tree/1.11.0" }, "time": "2022-06-03T18:03:27+00:00" - }, - { - "name": "yiisoft/injector", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/yiisoft/injector.git", - "reference": "0dc0127a7542341bdaabda7b85204e992938b83e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/yiisoft/injector/zipball/0dc0127a7542341bdaabda7b85204e992938b83e", - "reference": "0dc0127a7542341bdaabda7b85204e992938b83e", - "shasum": "" - }, - "require": { - "php": "^7.4|^8.0" - }, - "require-dev": { - "maglnet/composer-require-checker": "^3.8|^4.2", - "phpbench/phpbench": "^1.1", - "phpunit/phpunit": "^9.5", - "psr/container": "^1.0|^2.0", - "rector/rector": "^0.18.12", - "roave/infection-static-analysis-plugin": "^1.16", - "spatie/phpunit-watcher": "^1.23", - "vimeo/psalm": "^4.30|^5.7", - "yiisoft/test-support": "^1.2" - }, - "suggest": { - "psr/container": "For automatic resolving of dependencies" - }, - "type": "library", - "autoload": { - "psr-4": { - "Yiisoft\\Injector\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "description": "PSR-11 compatible injector. Executes a callable and makes an instances by injecting dependencies from a given DI container.", - "homepage": "https://www.yiiframework.com/", - "keywords": [ - "PSR-11", - "dependency injection", - "di", - "injector", - "reflection" - ], - "support": { - "chat": "https://t.me/yii3en", - "forum": "https://www.yiiframework.com/forum/", - "irc": "irc://irc.freenode.net/yii", - "issues": "https://github.com/yiisoft/injector/issues?state=open", - "source": "https://github.com/yiisoft/injector", - "wiki": "https://www.yiiframework.com/wiki/" - }, - "funding": [ - { - "url": "https://github.com/yiisoft", - "type": "github" - }, - { - "url": "https://opencollective.com/yiisoft", - "type": "open_collective" - } - ], - "time": "2023-12-20T09:39:03+00:00" } ], "aliases": [], diff --git a/src/Module/Environment/Architecture.php b/src/Module/Environment/Architecture.php index 2522b9e..06c3921 100644 --- a/src/Module/Environment/Architecture.php +++ b/src/Module/Environment/Architecture.php @@ -25,15 +25,26 @@ public static function create(): static public static function fromGlobals(): self { - return match (\php_uname('m')) { - 'AMD64', 'x86', 'x64', 'x86_64' => self::X86_64, + return self::tryFromString(\php_uname('m')) ?? throw new \OutOfRangeException( + \sprintf(self::ERROR_UNKNOWN_ARCH, \php_uname('m')), + ); + } + + public static function tryFromString(string $arch): ?self + { + return match ($arch) { + 'AMD64', 'amd64', 'x86', 'x64', 'x86_64' => self::X86_64, 'arm64', 'aarch64' => self::ARM_64, - default => throw new \OutOfRangeException( - \sprintf( - self::ERROR_UNKNOWN_ARCH, - \php_uname('m'), - ), - ), + default => null, }; } + + public static function tryFromBuildName(string $name): ?self + { + if (\preg_match('/\b(amd64|arm64|aarch64|x86_64|x64|x86)\b/i', $name, $matches)) { + return null; + } + + return self::tryFromString(\strtolower($matches[1])); + } } diff --git a/src/Module/Environment/OperatingSystem.php b/src/Module/Environment/OperatingSystem.php index c0d5bd8..8e91287 100644 --- a/src/Module/Environment/OperatingSystem.php +++ b/src/Module/Environment/OperatingSystem.php @@ -28,14 +28,30 @@ public static function create(): static public static function fromGlobals(): self { - return match (\PHP_OS_FAMILY) { + return self::tryFromString(\PHP_OS_FAMILY) ?? throw new \OutOfRangeException( + \sprintf(self::ERROR_UNKNOWN_OS, \PHP_OS_FAMILY), + ); + } + + public static function tryFromString(string $name): ?self + { + return match ($name) { 'Windows' => self::Windows, 'BSD' => self::BSD, 'Darwin' => self::Darwin, 'Linux' => \str_contains(\PHP_OS, 'Alpine') ? self::Alpine : self::Linux, - default => throw new \OutOfRangeException(\sprintf(self::ERROR_UNKNOWN_OS, \PHP_OS_FAMILY)), + default => null, }; } + + public static function tryFromBuildName(string $name): ?self + { + if (\preg_match('/\b(windows|linux|darwin|bsd|alpine)\b/i', $name, $matches)) { + return null; + } + + return self::tryFromString(\strtolower($matches[1])); + } } diff --git a/src/Module/Repository/Internal/Asset.php b/src/Module/Repository/Internal/Asset.php index df93f32..bb31e32 100644 --- a/src/Module/Repository/Internal/Asset.php +++ b/src/Module/Repository/Internal/Asset.php @@ -9,6 +9,10 @@ use Internal\DLoad\Module\Repository\AssetInterface; use Internal\DLoad\Module\Repository\ReleaseInterface; +/** + * @internal + * @psalm-internal Internal\DLoad\Module\Repository + */ abstract class Asset implements AssetInterface { /** @@ -17,10 +21,10 @@ abstract class Asset implements AssetInterface */ public function __construct( protected ReleaseInterface $release, - protected ?OperatingSystem $os, - protected ?Architecture $arch, protected string $name, protected string $uri, + protected ?OperatingSystem $os, + protected ?Architecture $arch, ) {} public function getRelease(): ReleaseInterface diff --git a/src/Module/Repository/Internal/AssetsCollection.php b/src/Module/Repository/Internal/AssetsCollection.php index 6082448..886ffee 100644 --- a/src/Module/Repository/Internal/AssetsCollection.php +++ b/src/Module/Repository/Internal/AssetsCollection.php @@ -10,6 +10,8 @@ /** * @template-extends Collection + * @internal + * @psalm-internal Internal\DLoad\Module\Repository */ final class AssetsCollection extends Collection { diff --git a/src/Module/Repository/Internal/GitHub/GitHubAsset.php b/src/Module/Repository/Internal/GitHub/GitHubAsset.php new file mode 100644 index 0000000..aef89df --- /dev/null +++ b/src/Module/Repository/Internal/GitHub/GitHubAsset.php @@ -0,0 +1,80 @@ +client->request('GET', $this->getUri(), [ + 'on_progress' => $progress, + ]); + + foreach ($this->client->stream($response) as $chunk) { + yield $chunk->getContent(); + } + } + + public function destroy(): void + { + unset($this->release, $this->client); + } +} diff --git a/src/Module/Repository/Internal/GitHub/GitHubRelease.php b/src/Module/Repository/Internal/GitHub/GitHubRelease.php new file mode 100644 index 0000000..54cb62b --- /dev/null +++ b/src/Module/Repository/Internal/GitHub/GitHubRelease.php @@ -0,0 +1,106 @@ + + * } + * + * @internal + * @psalm-internal Internal\DLoad\Module\Repository\GitHub + */ +final class GitHubRelease extends Release implements Destroyable +{ + /** + * @param non-empty-string $name + * @param non-empty-string $version + */ + public function __construct( + private HttpClientInterface $client, + GitHubRepository $repository, + string $name, + string $version, + ) { + parent::__construct($repository, $name, $version); + } + + /** + * @param GitHubReleaseApiResponse $data + */ + public static function fromApiResponse(GitHubRepository $repository, HttpClientInterface $client, array $data): self + { + isset($data['name']) or throw new \InvalidArgumentException( + 'Passed array must contain "name" value of type string.', + ); + + $name = self::getTagName($data); + $version = $data['tag_name'] ?? $data['name']; + $result = new self($client, $repository, $name, $version); + + $result->assets = AssetsCollection::from(static function () use ($client, $result, $data): \Generator { + /** @var GitHubAssetApiResponse $item */ + foreach ($data['assets'] ?? [] as $item) { + yield GitHubAsset::fromApiResponse($client, $result, $item); + } + }); + return $result; + } + + /** + * @return non-empty-string + * @throws ExceptionInterface + */ + public function getConfig(): string + { + $config = \vsprintf('https://raw.githubusercontent.com/%s/%s/.rr.yaml', [ + $this->getRepository()->getName(), + $this->getVersion(), + ]); + + return $this->client->request('GET', $config)->getContent(); + } + + public function destroy(): void + { + $this->assets === null or \array_walk($this->assets, static fn(object $asset) => + $asset instanceof Destroyable and $asset->destroy()); + + unset($this->assets, $this->repository, $this->client); + } + + /** + * Returns pretty-formatted tag (release) name. + * + * @note The return value is "pretty", but that does not mean that the tag physically exists. + * + * @param array{tag_name: string, name: string} $data + * @return string + */ + private static function getTagName(array $data): string + { + $parser = new VersionParser(); + + try { + return $parser->normalize($data['tag_name']); + } catch (\Throwable $e) { + try { + return $parser->normalize($data['name']); + } catch (\Throwable $e) { + return 'dev-' . $data['tag_name']; + } + } + } +} diff --git a/src/Module/Repository/Internal/GitHub/GitHubRepository.php b/src/Module/Repository/Internal/GitHub/GitHubRepository.php new file mode 100644 index 0000000..1ec383d --- /dev/null +++ b/src/Module/Repository/Internal/GitHub/GitHubRepository.php @@ -0,0 +1,150 @@ + + */ + private array $headers = [ + 'accept' => 'application/vnd.github.v3+json', + ]; + + /** + * @param non-empty-string $owner + * @param non-empty-string $repository + */ + public function __construct(string $owner, string $repository, HttpClientInterface $client = null) + { + $this->name = $owner . '/' . $repository; + $this->client = $client ?? HttpClient::create(); + } + + /** + * @param non-empty-string $package Package name in format "owner/repository" + */ + public static function fromDsn(string $package, HttpClientInterface $client = null): GitHubRepository + { + [$owner, $name] = $package; + return new GitHubRepository($owner, $name, $client); + } + + /** + * @throws ExceptionInterface + */ + public function getReleases(): ReleasesCollection + { + return $this->releases ??= ReleasesCollection::from(function () { + $page = 0; + + // Iterate over all pages + do { + $response = $this->releasesRequest(++$page); + + /** @psalm-var GitHubReleaseApiResponse $data */ + foreach ($response->toArray() as $data) { + yield GitHubRelease::fromApiResponse($this, $this->client, $data); + } + } while ($this->hasNextPage($response)); + }); + } + + /** + * @return string + */ + public function getName(): string + { + return $this->name; + } + + public function destroy(): void + { + $this->releases === null or \array_walk($this->releases, static fn(object $release) => + $release instanceof Destroyable and $release->destroy()); + + unset($this->releases, $this->client); + } + + /** + * @param string $method + * @param string $uri + * @param array $options + * @return ResponseInterface + * @throws TransportExceptionInterface + * @see HttpClientInterface::request() + */ + protected function request(string $method, string $uri, array $options = []): ResponseInterface + { + // Merge headers with defaults + $options['headers'] = \array_merge($this->headers, (array) ($options['headers'] ?? [])); + + return $this->client->request($method, $uri, $options); + } + + /** + * @param positive-int $page + * @throws TransportExceptionInterface + */ + private function releasesRequest(int $page): ResponseInterface + { + return $this->request('GET', $this->uri(self::URL_RELEASES), [ + 'query' => [ + 'page' => $page, + ], + ]); + } + + /** + * @param non-empty-string $pattern + * @return non-empty-string + */ + private function uri(string $pattern): string + { + return \sprintf($pattern, $this->getName()); + } + + /** + * @throws ExceptionInterface + */ + private function hasNextPage(ResponseInterface $response): bool + { + $headers = $response->getHeaders(); + $link = $headers['link'] ?? []; + + if (! isset($link[0])) { + return false; + } + + return \str_contains($link[0], 'rel="next"'); + } +} diff --git a/src/Module/Repository/Internal/Release.php b/src/Module/Repository/Internal/Release.php index 0bb88f7..21ffa64 100644 --- a/src/Module/Repository/Internal/Release.php +++ b/src/Module/Repository/Internal/Release.php @@ -9,6 +9,10 @@ use Internal\DLoad\Module\Repository\ReleaseInterface; use Internal\DLoad\Module\Repository\RepositoryInterface; +/** + * @internal + * @psalm-internal Internal\DLoad\Module\Repository + */ abstract class Release implements ReleaseInterface { /** @@ -31,11 +35,12 @@ public function __construct( protected RepositoryInterface $repository, string $name, protected string $version, + ?Stability $stability = null, iterable $assets = [], ) { $this->name = $this->simplifyReleaseName($name); $this->assets = AssetsCollection::create($assets); - $this->stability = $this->parseStability($version); + $this->stability = $stability ?? $this->parseStability($version); } public function getRepository(): RepositoryInterface diff --git a/src/Module/Repository/Internal/ReleasesCollection.php b/src/Module/Repository/Internal/ReleasesCollection.php index e11948e..707b578 100644 --- a/src/Module/Repository/Internal/ReleasesCollection.php +++ b/src/Module/Repository/Internal/ReleasesCollection.php @@ -10,6 +10,8 @@ /** * @template-extends Collection * @psalm-import-type StabilityType from Stability + * @internal + * @psalm-internal Internal\DLoad\Module\Repository */ final class ReleasesCollection extends Collection { diff --git a/src/Module/Repository/Internal/RepositoriesCollection.php b/src/Module/Repository/Internal/RepositoriesCollection.php index ebdded2..602bc40 100644 --- a/src/Module/Repository/Internal/RepositoriesCollection.php +++ b/src/Module/Repository/Internal/RepositoriesCollection.php @@ -6,6 +6,10 @@ use Internal\DLoad\Module\Repository\RepositoryInterface; +/** + * @internal + * @psalm-internal Internal\DLoad\Module\Repository + */ class RepositoriesCollection implements RepositoryInterface { /**