commit 1d23fba32825738a6b45a8b3c8ef934f13454e0a Author: airclay Date: Sat Jan 18 06:24:08 2025 +0000 first diff --git a/README.md b/README.md new file mode 100644 index 0000000..0c606ba --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +

UNIT3D Community Edition Installer

+ +

+ 🎉A Big Thanks To All Our Contributors and Sponsors🎉 +

+ +

NOTE: This only works for a fresh server with nothing on it but a new OS install!

+ +## This Repository +Installer for the [UNIT3D-Community-Edition](https://github.com/HDInnovations/UNIT3D-Community-Edition). + +**Officially Supported OS's** +- Ubuntu 22.04 LTS (Jammy Jellyfish) +- Ubuntu 20.04 LTS (Focal Fossa) + +**Unstable WIP** +- Ubuntu 24.04 LTS (Noble Numbat) + + +**We offer install and tuning services for a small price if not comfortable installing and tuninng server yourself. Otherwise if want to install yurself run commannd below.** + +**To install run the following:** (and follow the instructions. must be a fresh deicated server with nothing on it besides supported OS. Also must have a proper valid domain pointing to your server IP via A RECORD and CNAME for www) +``` +sudo apt -y install git +git clone https://github.com/HDInnovations/UNIT3D-Installer.git installer +cd installer +sudo ./install.sh +``` + +**NOTE: If you are running UNIT3D-Community-Edition on a non HTTPS instance you MUST change the following configs.** +``` +.env <-- SESSION_SECURE_COOKIE must be set to false +config/secure-headers.php <-- HTTP Strict Transport Security must be set to false +config/secure-headers.php <-- Content Security Policy must be disabled +``` + +### Suggestions and/or Bug Reporting +We encourage the use of [GitHub Issues](https://github.com/HDInnovations/UNIT3D-INSTALLER/issues/new)! diff --git a/artisan b/artisan new file mode 100755 index 0000000..2ec23de --- /dev/null +++ b/artisan @@ -0,0 +1,29 @@ +#!/usr/bin/env php +addCommands([ + new App\Commands\InstallCommand(), +]); + +// run the app +try { + $app->run(); +} catch (Exception $e) { + die($e->getMessage()); +} +?> \ No newline at end of file diff --git a/box.json b/box.json new file mode 100644 index 0000000..4ba983e --- /dev/null +++ b/box.json @@ -0,0 +1,21 @@ +{ + "chmod": "0755", + "directories": [ + "src" + ], + "files": [ + "" + ], + "finder": [ + { + "name": "*.php", + "exclude": [ + ], + "in": "vendor" + } + ], + "git-version": "package_version", + "main": "artisan", + "output": "unit3d.phar", + "stub": true +} \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..27bb242 --- /dev/null +++ b/composer.json @@ -0,0 +1,22 @@ +{ + "name": "unit3d/installer", + "description": "Installer for UNIT3D", + "type": "project", + "require": { + "php": ">=8.3", + "ext-dom": "*", + "symfony/console": "^4.4.49", + "symfony/process": "^4.4.44", + "symfony/yaml": "^4.4.45", + "symfony/var-dumper": "^4.4.47" + }, + "autoload": { + "psr-4": { + "App\\": "src/" + } + }, + "license": "MIT", + "require-dev": { + "phpunit/phpunit": "^9.6.15" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..b448a2f --- /dev/null +++ b/composer.lock @@ -0,0 +1,2670 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "9194be8084a0ed92cf83b6dbef6c37d0", + "packages": [ + { + "name": "psr/container", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "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/1.1.2" + }, + "time": "2021-11-05T16:50:12+00:00" + }, + { + "name": "symfony/console", + "version": "v4.4.49", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "33fa45ffc81fdcc1ca368d4946da859c8cdb58d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/33fa45ffc81fdcc1ca368d4946da859c8cdb58d9", + "reference": "33fa45ffc81fdcc1ca368d4946da859c8cdb58d9", + "shasum": "" + }, + "require": { + "php": ">=7.1.3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.8", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.1|^2" + }, + "conflict": { + "psr/log": ">=3", + "symfony/dependency-injection": "<3.4", + "symfony/event-dispatcher": "<4.3|>=5", + "symfony/lock": "<4.4", + "symfony/process": "<3.3" + }, + "provide": { + "psr/log-implementation": "1.0|2.0" + }, + "require-dev": { + "psr/log": "^1|^2", + "symfony/config": "^3.4|^4.0|^5.0", + "symfony/dependency-injection": "^3.4|^4.0|^5.0", + "symfony/event-dispatcher": "^4.3", + "symfony/lock": "^4.4|^5.0", + "symfony/process": "^3.4|^4.0|^5.0", + "symfony/var-dumper": "^4.3|^5.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/console/tree/v4.4.49" + }, + "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": "2022-11-05T17:10:16+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-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.4.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": "2023-05-23T14:45:45+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.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": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "42292d99c55abe617799667f454222c54c60e229" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", + "reference": "42292d99c55abe617799667f454222c54c60e229", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "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": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.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": "2023-07-28T09:04:16+00:00" + }, + { + "name": "symfony/polyfill-php72", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/70f4aebd92afca2f865444d30a4d2151c13c3179", + "reference": "70f4aebd92afca2f865444d30a4d2151c13c3179", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + } + }, + "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": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php72/tree/v1.28.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": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "fe2f306d1d9d346a7fee353d0d5012e401e984b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fe2f306d1d9d346a7fee353d0d5012e401e984b5", + "reference": "fe2f306d1d9d346a7fee353d0d5012e401e984b5", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "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": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.28.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": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.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": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/process", + "version": "v4.4.44", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "5cee9cdc4f7805e2699d9fd66991a0e6df8252a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/5cee9cdc4f7805e2699d9fd66991a0e6df8252a2", + "reference": "5cee9cdc4f7805e2699d9fd66991a0e6df8252a2", + "shasum": "" + }, + "require": { + "php": ">=7.1.3", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v4.4.44" + }, + "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": "2022-06-27T13:16:42+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v2.5.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c", + "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.1", + "symfony/deprecation-contracts": "^2.1|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + } + }, + "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/v2.5.2" + }, + "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": "2022-05-30T19:17:29+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v4.4.47", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "1069c7a3fca74578022fab6f81643248d02f8e63" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/1069c7a3fca74578022fab6f81643248d02f8e63", + "reference": "1069c7a3fca74578022fab6f81643248d02f8e63", + "shasum": "" + }, + "require": { + "php": ">=7.1.3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php72": "~1.5", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", + "symfony/console": "<3.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^3.4|^4.0|^5.0", + "symfony/process": "^4.4|^5.0", + "twig/twig": "^1.43|^2.13|^3.0.4" + }, + "suggest": { + "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", + "ext-intl": "To show region name in time zone dump", + "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "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 mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v4.4.47" + }, + "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": "2022-10-03T15:15:11+00:00" + }, + { + "name": "symfony/yaml", + "version": "v4.4.45", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "aeccc4dc52a9e634f1d1eebeb21eacfdcff1053d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/aeccc4dc52a9e634f1d1eebeb21eacfdcff1053d", + "reference": "aeccc4dc52a9e634f1d1eebeb21eacfdcff1053d", + "shasum": "" + }, + "require": { + "php": ">=7.1.3", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/console": "<3.4" + }, + "require-dev": { + "symfony/console": "^3.4|^4.0|^5.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v4.4.45" + }, + "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": "2022-08-02T15:47:23+00:00" + } + ], + "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:23:10+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2023-03-08T13:26:56+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.0.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4a21235f7e56e713259a6f76bf4b5ea08502b9dc", + "reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.0" + }, + "time": "2024-01-07T17:17:35+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.30", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ca2bd87d2f9215904682a9cb9bb37dda98e76089", + "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.30" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:47:57+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.6.15", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "05017b80304e0eb3f31d90194a563fd53a6021f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/05017b80304e0eb3f31d90194a563fd53a6021f1", + "reference": "05017b80304e0eb3f31d90194a563fd53a6021f1", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1 || ^2", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpunit/php-code-coverage": "^9.2.28", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.8", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.5", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^3.2", + "sebastian/version": "^3.0.2" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.6-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.15" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2023-12-01T16:55:19+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:19:30+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-05-07T05:35:17+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:03:51+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T06:03:37+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bde739e7565280bda77be70044ac1047bc007e34" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", + "reference": "bde739e7565280bda77be70044ac1047bc007e34", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-02T09:26:13+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-22T06:20:34+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:07:39+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:45:17+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.2", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", + "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.2" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2023-11-20T00:12:19+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=8.3", + "ext-dom": "*" + }, + "platform-dev": [], + "plugin-api-version": "2.6.0" +} diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..8744814 --- /dev/null +++ b/install.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash + +source tools/colors.sh + +# Detect OS +case $(head -n1 /etc/issue | cut -f 1 -d ' ') in + Ubuntu) type="ubuntu" ;; + *) type='' ;; +esac + +# Unable to detect OS Properly +# Note: OVH and other providers remove the contents of /etc/issue in their OS templates +# so we need to ask the user manually to tell us what the OS is as a Fallback +# Ref: https://github.com/ServNX/UNIT3D-INSTALLER/issues/8 +if [ "$type" = '' ]; then + echo -e "\n$Red We was unable to automatically determine your OS! $Color_Off" + echo -e "\n$Purple This can happen if you are using an OS template from a provider like OVH amongst others. $Color_Off\n" + + PS3='Please select the # for your OS: ' + options=("Ubuntu 24.04" "Ubuntu 22.04" "Ubuntu 20.04" "Quit") + select opt in "${options[@]}" + do + case $opt in + "Ubuntu 24.04") + echo 'Ubuntu 24.04 LTS \n \l' > /etc/issue + type='ubuntu' + break + ;; + "Ubuntu 22.04") + echo 'Ubuntu 22.04 LTS \n \l' > /etc/issue + type='ubuntu' + break + ;; + "Ubuntu 20.04") + echo 'Ubuntu 20.04 LTS \n \l' > /etc/issue + type='ubuntu' + break + ;; + "Quit") + exit 0 + ;; + *) + echo -e "$Red Invalid Option $REPLY $Color_Off" + ;; + esac + done +fi + +if [ -e $type.sh ]; then + bash ./$type.sh +fi diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..9733d01 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,21 @@ + + + + + ./tests + + + + + ./src + + + \ No newline at end of file diff --git a/src/BaseTestCase.php b/src/BaseTestCase.php new file mode 100644 index 0000000..90f69aa --- /dev/null +++ b/src/BaseTestCase.php @@ -0,0 +1,37 @@ +input = new ArgvInput(); + $this->output = new ConsoleOutput(); + + $this->io = new SymfonyStyle($this->input, $this->output); + } + + protected function getOsConfig($path) + { + + } + + protected function setOsConfig($path, $value) + { + + } +} \ No newline at end of file diff --git a/src/Classes/Config.php b/src/Classes/Config.php new file mode 100644 index 0000000..9de57ad --- /dev/null +++ b/src/Classes/Config.php @@ -0,0 +1,180 @@ +build(); + } + + public function get($path, $default = null) + { + $array = $this->values; + + if (!empty($path)) { + $keys = explode('.', $path); + foreach ($keys as $key) { + if (isset($array[$key])) { + $array = $array[$key]; + } else { + return $default; + } + } + } + + return $array; + } + + public function set($path, $value) + { + if (!empty($path)) { + $at = &$this->values; + $keys = explode('.', $path); + + while (count($keys) > 0) { + if (count($keys) === 1) { + if (is_array($at)) { + $at[array_shift($keys)] = $value; + } else { + throw new \RuntimeException("Can not set value at this path ($path) because is not array."); + } + } else { + $key = array_shift($keys); + + if (!isset($at[$key])) { + $at[$key] = array(); + } + + $at = &$at[$key]; + } + } + } else { + $this->values = $value; + } + + return $value; + } + + public function app($path, $value = null) + { + $path = "app.$path"; + return $value ? $this->set($path, $value) : $this->get($path); + } + + public function os($path, $value = null) + { + $path = 'os.' . distname() . '.' . $path; + return $value ? $this->set($path, $value) : $this->get($path); + } + + public function add($path, array $values) + { + $get = (array)$this->get($path); + $this->set($path, $this->arrayMergeRecursiveDistinct($get, $values)); + } + + public function have($path) + { + $keys = explode('.', $path); + $array = $this->values; + foreach ($keys as $key) { + if (isset($array[$key])) { + $array = $array[$key]; + } else { + return false; + } + } + + return true; + } + + public function setValues($values) + { + $this->values = $values; + } + + public function getValues() + { + return $this->values; + } + + public function search($searchKey, $array = null) + { + if ($array == null) { + $array = $this->values; + } + + //create a recursive iterator to loop over the array recursively + $iter = new RecursiveIteratorIterator( + new RecursiveArrayIterator($array), + RecursiveIteratorIterator::SELF_FIRST); + + //loop over the iterator + foreach ($iter as $key => $value) { + //if the key matches our search + if ($key === $searchKey) { + //add the current key + $keys = array($key); + //loop up the recursive chain + for ($i = $iter->getDepth() - 1; $i >= 0; $i--) { + //add each parent key + array_unshift($keys, $iter->getSubIterator($i)->key()); + } + //return our output array + return array('path' => implode('.', $keys), 'value' => $value); + } + } + + //return false if not found + return false; + } + + protected function arrayMergeRecursiveDistinct(array &$array1, array &$array2) + { + $merged = $array1; + + foreach ($array2 as $key => &$value) { + if (is_array($value) && isset ($merged[$key]) && is_array($merged[$key])) { + if (is_int($key)) { + $merged[] = $this->arrayMergeRecursiveDistinct($merged[$key], $value); + } else { + $merged[$key] = $this->arrayMergeRecursiveDistinct($merged[$key], $value); + } + } else { + if (is_int($key)) { + $merged[] = $value; + } else { + $merged[$key] = $value; + } + } + } + + return $merged; + } + + protected function build() + { + $directory = __DIR__ . '/../Configs'; + + $files = scandir($directory); + + foreach ($files as $file) { + if (is_file("$directory/$file")) { + $conf = require $directory . "/$file"; + $this->values[basename($file, '.php')] = $conf; + } + } + } + +} \ No newline at end of file diff --git a/src/Classes/Process.php b/src/Classes/Process.php new file mode 100644 index 0000000..48cf960 --- /dev/null +++ b/src/Classes/Process.php @@ -0,0 +1,127 @@ +io = $io; + } + + /** + * Executes a new Process instance + * + * @param string $cmd + * @return string + */ + public function execute($command, array $input = null, $force = false, $timeout = 3600, $cwd = null, array $env = null) + { + + $this->io->writeln("\n$command"); + + $process = new SymfonyProcess($command, $cwd, $env, null, $timeout); + $process->setIdleTimeout(900); + + $inputStream = null; + if ($input !== null && is_array($input)) { + $inputStream = new InputStream(); + $process->setInput($inputStream); + + !$this->debug ?: $this->io->writeln('[debug] Pty is on'); + $process->setPty(true); + } + + $bar = $this->progressStart(); + + $process->start(); + + $process->wait(function ($type, $buffer) use ($bar, $input, $inputStream) { + !$this->debug ?: $this->io->writeln("[debug] $buffer"); + + if ($input !== null && is_array($input)) { + + $last = null; + foreach($input as $expect => $send) { + if (str_contains($buffer, $expect) && $expect !== $last) { + $inputStream->write($send . "\n"); + $last = $expect; + } + + usleep(5000); + } + + } + + $bar->advance(); + usleep(200000); + }); + + $this->progressStop($bar); + $process->stop(); + + if (!$process->isSuccessful()) { + if (!$force) { + $this->io->error($process->getErrorOutput()); + die(); + } + + $this->io->writeln("\n[Warning] " . $process->getErrorOutput()); + } + + return $process; + } + + /** + * @return ProgressBar + */ + protected function progressStart() + { + $bar = $this->io->createProgressBar(); + $bar->setBarCharacter('='); + $bar->setFormat('[%bar%] (%message%)'); + $bar->setMessage('Please Wait ...'); + //$bar->setRedrawFrequency(20); todo: may be useful for platforms like CentOS + $bar->start(); + + return $bar; + } + + /** + * @param $bar + */ + protected function progressStop(ProgressBar $bar) + { + $bar->setMessage("Done!"); + $bar->finish(); + } + + /** + * @param bool $debug + */ + public function setDebug(bool $debug) + { + $this->debug = $debug; + } +} diff --git a/src/Commands/InstallCommand.php b/src/Commands/InstallCommand.php new file mode 100644 index 0000000..53cd445 --- /dev/null +++ b/src/Commands/InstallCommand.php @@ -0,0 +1,107 @@ + 'Validating Installer Policies', + ServerSetup::class => 'Server Setup', + Prerequisites::class => 'Prerequisites', + DatabaseSetup::class => 'Configuring & Securing Database', + PhpSetup::class => 'PHP & PHP-FPM Configuration', + NginxSetup::class => 'Nginx Setup & Configurations', + Unit3dSetup::class => 'UNIT3D-Community-Edition Settings and Configuration', + ]; + + /** + * @var SymfonyStyle + */ + private $io; + + /** + * @var Config + */ + private $config; + + protected function configure() + { + $this + ->setName('install') + ->setDescription('Provisions Server') + ->setHelp('Provisions Server and installs the UNIT3D Platform.'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->io = new SymfonyStyle($input, $output); + $this->config = new Config(); + + $this->displayIntro(); + + foreach ($this->steps as $class => $header) { + $this->head($header); + + (new $class($this->io, $this->config))->handle(); + + $this->done(); + } + + $this->head("Finalizing Install"); + + $this->info(); + + $this->done(); + } + + private function info() + { + $db = $this->config->app('db'); + $dbuser = $this->config->app('dbuser'); + $dbpass = $this->config->app('dbpass'); + $domain = $this->config->app('hostname'); + $owner = $this->config->app('owner'); + $password = $this->config->app('password'); + + $this->io->writeln([ + 'Please run "certbot renew --dry-run" manually to test your LetsEncrypt renewal process!!!', + ]); + + $this->io->note([ + 'Database: ' . $db, + 'Database User: ' . $dbuser, + 'Database Password: ' . $dbpass + ]); + + $this->io->writeln([ + 'UNIT3D-Community-Edition has been successfully installed!', + ' ', + "Visit $domain in a browser", + ' ', + "Login: $owner", + "Password: $password" + ]); + } + + private function done() + { + $this->io->writeln("[OK] Done!"); + } +} diff --git a/src/Configs/app.php b/src/Configs/app.php new file mode 100644 index 0000000..6066110 --- /dev/null +++ b/src/Configs/app.php @@ -0,0 +1,53 @@ + '8.2', + + 'repository' => 'https://github.com/HDInnovations/UNIT3D-Community-Edition.git', + + 'database_installers' => [ + /** + * Map to the Installer class + */ + 'MySql' => MySqlSetup::class, + ], + + /* + * Dynamically set configuration defaults and placeholders + * + * These do NOT need policy classes + */ + + /* Main Server */ + 'server_name' => '', + 'ip' => '', + 'hostname' => '', + 'ssl' => true, + 'owner' => '', + 'owner_email' => '', + 'password' => '', + + /* Database */ + 'database_driver' => 'MySql', + + 'db' => '', + 'dbuser' => '', + 'dbpass' => '', + 'dbrootpass' => '', + + /* Mail */ + 'mail_driver' => 'smtp', + 'mail_host' => '', + 'mail_port' => '', + 'mail_username' => '', + 'mail_password' => '', + 'mail_from_name' => '', + + /* Chat */ + 'echo-port' => '', + + /* API Keys */ + 'tmdb-key' => '', +]; diff --git a/src/Configs/os.php b/src/Configs/os.php new file mode 100644 index 0000000..d084ff0 --- /dev/null +++ b/src/Configs/os.php @@ -0,0 +1,32 @@ + [ + 'pkg_manager' => 'apt-get', + 'web-user' => 'www-data', + 'install_dir' => '/var/www/html', + 'nginx-sites-available_path' => '/etc/nginx/sites-available', + + 'software' => [ + 'build-essential' => 'Basic C/C++ Development Environment', + 'nginx' => 'Web Server', + 'mysql-server' => 'Database Server', + 'supervisor' => 'A Process Control System', + 'nodejs' => 'JavaScript Run-time Environment (Includes npm)', + 'git' => 'Version Control', + 'tmux' => 'Screen Multiplexer', + 'vim' => 'Text Editor', + 'wget' => 'Transfer Data From A Server', + 'zip' => 'Compress Files', + 'unzip' => 'Decompress Files', + 'htop' => 'Monitor Server Resources', + 'cron' => 'Process Scheduling Daemon', + ], + ] + + +]; diff --git a/src/Helpers/helpers.php b/src/Helpers/helpers.php new file mode 100644 index 0000000..4b4b3c3 --- /dev/null +++ b/src/Helpers/helpers.php @@ -0,0 +1,199 @@ + $value) { + //if the key matches our search + if ($key === $searchKey) { + //add the current key + $keys = array($key); + //loop up the recursive chain + for ($i = $iter->getDepth() - 1; $i >= 0; $i--) { + //add each parent key + array_unshift($keys, $iter->getSubIterator($i)->key()); + } + //return our output array + return array('path' => implode('.', $keys), 'value' => $value); + } + } + + //return false if not found + return false; + } +} + +if (!function_exists('distinfo')) { + function distinfo() + { + $distname = strtolower(trim(shell_exec('head -n1 /etc/issue | cut -f 1 -d \' \''))); + $distver = trim(shell_exec('head -n1 /etc/issue | cut -f 2 -d \' \'')); + $lts = (trim(shell_exec('head -n1 /etc/issue | cut -f 3 -d \' \'') === 'LTS')); + + preg_match("/^[0-9]..[0-9]./m", $distver, $matches); + $mainver = $matches[0]; + + switch ($mainver) { + case "24.04": + $relname = "(Noble Numbat)"; + break; + case "22.04": + $relname = "(Jammy Jellyfish)"; + break; + case "20.04": + $relname = "(Focal Fossa)"; + break; + default: + $relname = "UNKNOWN"; + } + + return array( + 'name' => $distname, + 'version' => $distver, + 'mainver' => $mainver, + 'relname' => $relname, + 'lts' => $lts + ); + } +} + +if (!function_exists('distname')) { + function distname() + { + return strtolower(distinfo()['name']); + } +} + +if (!function_exists('distversion')) { + function distversion() + { + return strtolower(distinfo()['version']); + } +} + +if (!function_exists('distmainver')) { + function distmainver() + { + return strtolower(distinfo()['mainver']); + } +} + +if (!function_exists('distrelname')) { + function distrelname() + { + return strtolower(distinfo()['name']); + } +} + +if (!function_exists('distlts')) { + function distlts() + { + return strtolower(distinfo()['lts']); + } +} + +if (!function_exists('memory')) { + function memory() + { + return shell_exec("grep 'MemTotal' /proc/meminfo |tr ' ' '\n' |grep [0-9]") != ''; + } +} diff --git a/src/Installer/BaseInstaller.php b/src/Installer/BaseInstaller.php new file mode 100644 index 0000000..dc45dda --- /dev/null +++ b/src/Installer/BaseInstaller.php @@ -0,0 +1,83 @@ +io = $io; + $this->process = new Process($this->io); + $this->config = $config; + $this->pkg_manager = $config->get('os.'.distname().'.pkg_manager'); + } + + abstract public function handle(); + + protected function install($pkgs) + { + $this->process->execute($this->pkg_manager . " install -y $pkgs"); + } + + protected function process(array $commands, $force = false) + { + foreach ($commands as $cmd) { + $this->process->execute($cmd, null, $force); + } + } + + protected function createFromStub(array $fr, $stub, $to) + { + $stub = resource_path() . distname() . '/' . $stub; + + $file = file_get_contents($stub); + + if ($file === false) { + $this->throwError("'$stub' error getting file contents. Please report this bug."); + } + + $contents = str_replace(array_keys($fr), array_values($fr), $file); + + file_put_contents($to, $contents); + return true; + } + + protected function setTimeout($timeout) + { + $this->timeout = $timeout; + return $this; + } + +} \ No newline at end of file diff --git a/src/Installer/Database/DatabaseSetup.php b/src/Installer/Database/DatabaseSetup.php new file mode 100644 index 0000000..9270253 --- /dev/null +++ b/src/Installer/Database/DatabaseSetup.php @@ -0,0 +1,16 @@ +config->app('database_driver'); + $class = $this->config->app('database_installers.' . $driver); + + (new $class($this->io, $this->config))->handle(); + } +} \ No newline at end of file diff --git a/src/Installer/Database/MySqlSetup.php b/src/Installer/Database/MySqlSetup.php new file mode 100644 index 0000000..c900459 --- /dev/null +++ b/src/Installer/Database/MySqlSetup.php @@ -0,0 +1,81 @@ +config->app('dbrootpass'); + $db = $this->config->app('db'); + $dbuser = $this->config->app('dbuser'); + $dbpass = $this->config->app('dbpass'); + + switch (true) { + case (memory() >= 1200000 && memory() < 3900000): + $mycnf = "my-medium.cnf"; + break; + case (memory() >= 3900000): + $mycnf = "my-large.cnf"; + break; + default: + $mycnf = "my-small.cnf"; + } + + $this->process(['cp -f ' . resource_path(distname() . "/mysql/$mycnf") . ' /etc/mysql/my.cnf']); + + if (distname() === 'ubuntu') { + if ((distmainver() === '20.04' || distmainver() === '22.04' || distmainver() === '24.04') && !is_dir('/var/lib/mysql')) { + $this->process([ + 'mkdir /var/lib/mysql', + 'chown mysql:mysql /var/lib/mysql', + 'mysqld --initialize-insecure', + ]); + } + } + + $this->createFromStub( + [ + '{{PASSWORD}}' => $root_pass + ], + 'mysql/.my.cnf', + '/root/.my.cnf' + ); + + $this->process([ + 'update-rc.d mysql defaults', + 'service mysql start', + "mysqladmin -u root password $root_pass", + 'chmod 600 /root/.my.cnf' + ]); + + /* + * Critical + */ + $this->process([ + "mysql -e \"DROP USER IF EXISTS $dbuser\"", + "mysql -e \"DROP DATABASE IF EXISTS $db\"", + "mysql -e \"CREATE DATABASE $db\"", + "mysql -e \"CREATE USER '$dbuser'@'localhost' IDENTIFIED BY '$dbpass';\"", + "mysql -e \"GRANT ALL PRIVILEGES ON $db . * TO '$dbuser'@'localhost'\"", + "mysql -e \"ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '$root_pass'\"", + "mysql -e \"DELETE FROM mysql.user WHERE User=''\"", + "mysql -e \"DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1')\"", + ]); + + /* + * Non-Critical + */ + $this->process([ + "mysql -e \"DROP DATABASE IF EXISTS test\"", + "mysql -e \"DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%'\"", + ], true); + + + $this->process(["mysql -e \"FLUSH PRIVILEGES\""]); + + $this->io->writeln(' '); + } +} diff --git a/src/Installer/Nginx/NginxSetup.php b/src/Installer/Nginx/NginxSetup.php new file mode 100644 index 0000000..d8f601f --- /dev/null +++ b/src/Installer/Nginx/NginxSetup.php @@ -0,0 +1,44 @@ +config->os('nginx-sites-available_path') . '/default'; + $echo_port = $this->config->app('echo-port'); + $fqdn = $this->config->app('hostname'); + $email = $this->config->app('owner_email'); + $ssl = $this->config->app('ssl'); + + if (file_exists($default)) { + $this->process(["rm -rf $default"]); + } + + $this->createFromStub([ + '{{FQDN}}' => $fqdn + ], 'nginx/default.site', $default); + + $this->process([ + "ufw allow 'Nginx Full'", + "ufw delete allow 'Nginx HTTP'", + "ufw allow $echo_port", + "ufw enable", + "systemctl restart nginx" + ]); + + $this->install('certbot python3-certbot-nginx'); + + if ($ssl == 'yes') { + $this->process([ + "certbot --redirect --nginx -n --agree-tos --email=$email -d $fqdn -d www.$fqdn --rsa-key-size 2048", + ]); + } + + $this->io->writeln(' '); + } +} diff --git a/src/Installer/PHP/PhpSetup.php b/src/Installer/PHP/PhpSetup.php new file mode 100644 index 0000000..e3ed9e3 --- /dev/null +++ b/src/Installer/PHP/PhpSetup.php @@ -0,0 +1,49 @@ +config->app('hostname'); + + $pool = trim(shell_exec('find /etc/php* -type d \( -name "pool.d" -o -name "*fpm.d" \)')); + $php_fpm = trim(shell_exec('ls /etc/init.d/php*.*-fpm* |cut -f 4 -d /')); + + $this->createFromStub( + [ + '{{FQDN}}' => $this->config->app('hostname'), + '{{WEBUSER}}' => strtolower($this->config->os('web-user')), + ], + 'php-fpm/php-fpm.conf', + "$pool/$fqdn.conf" + ); + + //$this->process(["cp -f " . resource_path() . distname() . "/php-fpm/php-fpm.sock $pool/",]); + + if (!is_link("/etc/init.d/php-fpm")) { + $this->process(["ln -s /etc/init.d/$php_fpm /etc/init.d/php-fpm > /dev/null 2>&1"]); + } + + foreach (explode("\n", trim(shell_exec('find /etc/php* -name php.ini'))) as $file) { + $this->process([ + "sed -i 's/;date.timezone =/date.timezone = UTC/g' $file", + "sed -i 's%_open_tag = Off%_open_tag = On%g' $file", + "sed -i 's/;cgi.fix_pathinfo=1/cgi.fix_pathinfo=1/g' $file" + ]); + + } + + $this->process([ + "systemctl restart $php_fpm", + "update-rc.d $php_fpm defaults", + "service $php_fpm start" + ]); + + $this->io->writeln(' '); + } +} \ No newline at end of file diff --git a/src/Installer/Policies/AppKeyExists.php b/src/Installer/Policies/AppKeyExists.php new file mode 100644 index 0000000..ca53d55 --- /dev/null +++ b/src/Installer/Policies/AppKeyExists.php @@ -0,0 +1,18 @@ +config->getValues())) { + $this->throwError( + "The key 'app' was not found in config! + + Inside the 'Configs' directory there should be a file named 'app.php' + with the application configuration." + ); + } + } +} \ No newline at end of file diff --git a/src/Installer/Policies/AppNotInstalled.php b/src/Installer/Policies/AppNotInstalled.php new file mode 100644 index 0000000..34eca4d --- /dev/null +++ b/src/Installer/Policies/AppNotInstalled.php @@ -0,0 +1,13 @@ +config->os('install_dir') . DIRECTORY_SEPARATOR . 'app')) { + $this->throwError('UNIT3D-Community-Edition already installed ... Exiting installer'); + } + } +} \ No newline at end of file diff --git a/src/Installer/Policies/BasePolicy.php b/src/Installer/Policies/BasePolicy.php new file mode 100644 index 0000000..f624171 --- /dev/null +++ b/src/Installer/Policies/BasePolicy.php @@ -0,0 +1,36 @@ +io = $io; + + $this->config = $config; + } + + public function handle($param = null) + { + $this->allows($param); + } + + abstract public function allows($param = null); +} \ No newline at end of file diff --git a/src/Installer/Policies/DatabaseDriverKeyExists.php b/src/Installer/Policies/DatabaseDriverKeyExists.php new file mode 100644 index 0000000..a0d4309 --- /dev/null +++ b/src/Installer/Policies/DatabaseDriverKeyExists.php @@ -0,0 +1,42 @@ +config->get('app'))) { + $this->throwError("The key 'database_driver' was not found in 'app' config! + + Please fix this and try again. + + Example: 'database_driver' => 'MySql'," + ); + } + + $driver = $this->config->app('database_driver'); + + if (!is_string($driver)) { + $this->throwError("The key 'database_driver' is NOT a string in 'app' config! + + 'database_driver' should be a string value of the Default database driver. + + Example: 'database_driver' => 'MySql'," + ); + } + + if (!array_key_exists($driver, $this->config->app('database_installers'))) { + $this->throwError("The key 'database_driver' is invalid in 'app' config! + + 'database_driver' should match a key listed in 'database_installers' array. + + Example: 'database_driver' => 'MySql', + + 'database_installers' => [ + 'MySql' => MySqlInstaller::class, + ]," + ); + } + } +} \ No newline at end of file diff --git a/src/Installer/Policies/DatabaseInstallersKeyExists.php b/src/Installer/Policies/DatabaseInstallersKeyExists.php new file mode 100644 index 0000000..8cee223 --- /dev/null +++ b/src/Installer/Policies/DatabaseInstallersKeyExists.php @@ -0,0 +1,49 @@ +config->get('app'))) { + $this->throwError( + "The key 'database_installers' was not found in 'app' config! + + Please fix this and try again. + + Example: 'database_installers' => [ + 'MySql' => MySqlInstaller::class, + ]," + ); + } + + $database_installers = $this->config->app('database_installers'); + + if (!is_array($database_installers)) { + $this->throwError( + "The key 'database_installers' is NOT an array in 'app' config! + + 'database_installers' should be an array of database drivers + mapped with their respected installer class. + + Example: 'database_installers' => [ + 'MySql' => MySqlInstaller::class, + ]," + ); + } + + if (count($database_installers) <= 0) { + $this->throwError( + "The key 'database_installers' is an empty array in 'app' config! + + 'database_installers' should be an array of at least 1 database driver + mapped with its respected installer class. + + Example: 'database_installers' => [ + 'MySql' => MySqlInstaller::class, + ]," + ); + } + } +} \ No newline at end of file diff --git a/src/Installer/Policies/InstallDirKeyExists.php b/src/Installer/Policies/InstallDirKeyExists.php new file mode 100644 index 0000000..b57a5dc --- /dev/null +++ b/src/Installer/Policies/InstallDirKeyExists.php @@ -0,0 +1,19 @@ +config->get('os.' . distname()))) { + $this->throwError( + "The key 'install_dir' was not found in 'app' config! + + Please fix this and try again. + + Example: 'install_dir' => '/var/www/html'," + ); + } + } +} \ No newline at end of file diff --git a/src/Installer/Policies/InstallerPolicies.php b/src/Installer/Policies/InstallerPolicies.php new file mode 100644 index 0000000..90b9d78 --- /dev/null +++ b/src/Installer/Policies/InstallerPolicies.php @@ -0,0 +1,31 @@ +steps as $class) { + (new $class($this->io, $this->config))->allows($param); + } + } +} \ No newline at end of file diff --git a/src/Installer/Policies/IsPhpVersionCompat.php b/src/Installer/Policies/IsPhpVersionCompat.php new file mode 100644 index 0000000..92cb10a --- /dev/null +++ b/src/Installer/Policies/IsPhpVersionCompat.php @@ -0,0 +1,14 @@ +config->app('min_php_version'); + if (version_compare(PHP_VERSION, $phpv, '<')) { + $this->throwError('PHP Version is not compatible with UNIT3D-Community-Edition. Install PHP ' . $phpv . ' or later...'); + } + } +} \ No newline at end of file diff --git a/src/Installer/Policies/IsPrivilegedUser.php b/src/Installer/Policies/IsPrivilegedUser.php new file mode 100644 index 0000000..baffb8a --- /dev/null +++ b/src/Installer/Policies/IsPrivilegedUser.php @@ -0,0 +1,14 @@ +throwError('Must be ran as root or using sudo!'); + } + } +} \ No newline at end of file diff --git a/src/Installer/Policies/PhpVersionKeyExists.php b/src/Installer/Policies/PhpVersionKeyExists.php new file mode 100644 index 0000000..5503a08 --- /dev/null +++ b/src/Installer/Policies/PhpVersionKeyExists.php @@ -0,0 +1,19 @@ +config->get('app'))) { + $this->throwError( + "The key 'min_php_version' was not found in 'app' config! + + Please fix this and try again. + + Example: 'min_php_version' => '8.1'," + ); + } + } +} \ No newline at end of file diff --git a/src/Installer/Prerequisites/Prerequisites.php b/src/Installer/Prerequisites/Prerequisites.php new file mode 100644 index 0000000..6f04b2e --- /dev/null +++ b/src/Installer/Prerequisites/Prerequisites.php @@ -0,0 +1,36 @@ +config->os('software'); + + $this->io->writeln("!! WARNING !! We are preparing to install software on your server. Please review and confirm!\n"); + $this->seperator(); + + foreach ($software as $pkg => $desc) { + $this->io->writeln("* '$pkg': $desc"); + } + $this->seperator(); + + if (!$this->io->confirm('Do you wish to continue?', true)) { + $this->throwError('Aborted ...'); + }; + + $this->process(['curl -sL https://deb.nodesource.com/setup_20.x | sudo -E bash -']); + + $pkgs = implode(' ', array_keys($software)); + $this->install($pkgs); + + $this->process(['npm install -g laravel-echo-server']); + $this->install('ufw'); + $this->process(['ufw allow 8443']); + + $this->io->writeln(''); + } +} diff --git a/src/Installer/Server/ServerSetup.php b/src/Installer/Server/ServerSetup.php new file mode 100644 index 0000000..0136ac2 --- /dev/null +++ b/src/Installer/Server/ServerSetup.php @@ -0,0 +1,166 @@ +server(); + + $this->user(); + + $this->database(); + + $this->mail(); + + $this->chat(); + + $this->apiKeys(); + } + + protected function server() + { + $server_name = $this->question('Server Name', hostname()); + $this->config->app('server_name', trim($server_name)); + + do { + $hostname = strtolower($this->question('The domain your going to use. ( Example: example.com )', fqdn())); + + $valid = (str_contains($hostname, '.') || $hostname === 'localhost'); + + if (!$valid) { + $this->warning("Invalid Format:"); + $this->io->writeln("Must be a fully qualified domain name. Examples:"); + $this->io->listing([ + fqdn() . '.com', + 'server.' . fqdn() . '.com', + 'example.com', + 'server.example.com', + 'localhost' + ]); + } + + } while (!$valid); + + $this->config->app('hostname', trim($hostname)); + + $ip = $this->question('Primary IP Address', ip()); + $this->config->app('ip', trim($ip)); + + $ssl = $this->io->choice('Enable SSL (https)', ['yes', 'no'], 'yes'); + $this->config->app('ssl', $ssl); + + } + + protected function user() + { + $this->io->writeln('User Settings'); + $this->seperator(); + + $dbowner = $this->question('Owner Username', ''); + $this->config->app('owner', $dbowner); + + $dbpass = $this->question('Owner Password', ''); + $this->config->app('password', $dbpass); + + $default = 'admin@' . $this->config->app('hostname'); + $email = $this->question('Owner Email', $default); + $this->config->app('owner_email', trim($email)); + } + + protected function database() + { + $this->io->writeln('Database Settings'); + $this->seperator(); + + $driver_choices = array_keys($this->config->app('database_installers')); + $default_driver = $this->config->app('database_driver'); + + $driver = $this->io->choice('Choose a database driver', $driver_choices, $default_driver); + $this->config->app('database_driver', $driver); + + $this->io->writeln('Special Characters Are Not Working At This Time!'); + $db_root_pass = $this->question('DB Server Root Password', ''); + $this->config->app('dbrootpass', $db_root_pass); + + $db = $this->question('UNIT3D DB Name', 'unit3d'); + $this->config->app('db', $db); + + $dbuser = $this->question('UNIT3D DB User', 'unit3d'); + $this->config->app('dbuser', $dbuser); + + $this->io->writeln('Special Characters Are Not Working At This Time!'); + $dbpass = $this->question('UNIT3D DB Password', ''); + $this->config->app('dbpass', $dbpass); + } + + protected function chat() + { + $this->io->writeln('Chat Settings'); + $this->seperator(); + + $port = $this->question('Chat Listening Port', '8443'); + $this->config->app('echo-port', $port); + } + + protected function apiKeys() + { + $this->io->writeln('API Keys'); + $this->seperator(); + + $this->io->writeln('Obtaining an TMDB Key:'); + $this->io->listing([ + 'Visit https://www.themoviedb.org/', + 'Create Free Account', + 'Visit https://www.themoviedb.org/settings/api' + ]); + + $key = $this->question('TMDB Key', ''); + $this->config->app('tmdb-key', $key); + } + + protected function mail() + { + $this->io->writeln('Mail Settings'); + $this->io->writeln('(Used for things like invites, registration, ect.)'); + $this->seperator(); + + $this->io->writeln('/* You will need a provider like Resend. */'); + $this->io->writeln('https://resend.com'); + + $this->io->writeln('Ref: https://laravel.com/docs/11.x/mail#introduction'); + + $value = $this->io->choice('Mail Driver', [ + "smtp", + "sendmail", + "mailgun", + "mandrill", + "ses", + "sparkpost", + "log", + "array" + ], 'smtp'); + + $this->config->app('mail_driver', $value); + + $value = $this->question('Mail Host', ''); + $this->config->app('mail_host', $value); + + $value = $this->question('Mail Port', '587'); + $this->config->app('mail_port', $value); + + $value = $this->question('Mail Username', ''); + $this->config->app('mail_username', $value); + + $value = $this->question('Mail Password', ''); + $this->config->app('mail_password', $value); + + $value = $this->question('Mail From Name', ''); + $this->config->app('mail_from_name', $value); + + } +} diff --git a/src/Installer/UNIT3D/Unit3dSetup.php b/src/Installer/UNIT3D/Unit3dSetup.php new file mode 100644 index 0000000..26e5348 --- /dev/null +++ b/src/Installer/UNIT3D/Unit3dSetup.php @@ -0,0 +1,160 @@ +clone(); + + $this->env(); + + $this->perms(); + + $this->crons(); + + $this->setup(); + + } + + protected function clone() + { + $this->io->writeln('Cloning Source Files'); + $this->seperator(); + + $install_dir = $this->config->os('install_dir'); + $url = $this->config->app('repository'); + + if (is_dir($install_dir)) { + $this->process(["rm -rf $install_dir"]); + } + + $this->process(["git clone $url $install_dir"]); + + if (!is_dir($install_dir)) { + $this->throwError('Something went wrong with the cloning process. Please report this bug!'); + } + } + + protected function env() + { + $this->io->writeln("\n\nPreparing the '.env' File"); + $this->seperator(); + + $install_dir = $this->config->os('install_dir'); + + if (file_exists("$install_dir/.env")) { + $this->process(["rm $install_dir/.env"]); + } + + $this->createFromStub( + [ + '{{PROTOCOL}}' => $this->config->app('ssl') == 'yes' ? 'https' : 'http', + '{{FQDN}}' => $this->config->app('hostname'), + '{{DBDRIVER}}' => strtolower($this->config->app('database_driver')), + '{{DB}}' => $this->config->app('db'), + '{{DBUSER}}' => $this->config->app('dbuser'), + '{{DBPASS}}' => $this->config->app('dbpass'), + '{{OWNER}}' => $this->config->app('owner'), + '{{OWNEREMAIL}}' => $this->config->app('owner_email'), + '{{OWNERPASSWORD}}' => $this->config->app('password'), + '{{TMDBAPIKEY}}' => $this->config->app('tmdb-key'), + '{{MAILDRIVER}}' => $this->config->app('mail_driver'), + '{{MAILHOST}}' => $this->config->app('mail_host'), + '{{MAILPORT}}' => $this->config->app('mail_port'), + '{{MAILUSERNAME}}' => $this->config->app('mail_username'), + '{{MAILPASSWORD}}' => $this->config->app('mail_password'), + '{{MAILFROMNAME}}' => $this->config->app('mail_from_name') + ], + '../.env.stub', + "$install_dir/.env" + ); + + $this->io->writeln('OK'); + } + + protected function perms() + { + $this->io->writeln("\nSetting Permissions"); + $this->seperator(); + + $install_dir = $this->config->os('install_dir'); + $web_user = $this->config->os('web-user'); + + $this->process([ + "chown -R $web_user:$web_user /etc/letsencrypt", + "chown -R $web_user:$web_user " . dirname($install_dir), + "find $install_dir -type d -exec chmod 0775 '{}' + -or -type f -exec chmod 0664 '{}' +", + "chmod 750 $install_dir/artisan", + "chmod 640 $install_dir/.env" + ]); + } + + protected function setup() + { + $this->io->writeln("\n\nSetting Up Web Site"); + $this->seperator(); + + $install_dir = $this->config->os('install_dir'); + $fqdn = $this->config->app('hostname'); + $web_user = $this->config->os('web-user'); + $echo_port = $this->config->app('echo-port'); + $protocol = $this->config->app('ssl') == 'yes' ? 'https' : 'http'; + + $this->createFromStub([ + '{{FQDN}}' => $fqdn, + '{{PORT}}' => $echo_port, + '{{PROTOCOL}}' => $protocol, + ], '../laravel-echo-server.stub', '/var/www/html/laravel-echo-server.json'); + + $this->process([ + "chown -R $web_user:$web_user $install_dir/laravel-echo-server.json", + ]); + + $this->createFromStub([ + '{{INSTALLDIR}}' => $install_dir, + '{{WEBUSER}}' => $web_user, + ], 'supervisor/app.conf', '/etc/supervisor/conf.d/unit3d.conf'); + + $this->process([ + 'supervisorctl reread', + 'supervisorctl update', + 'supervisorctl reload' + ]); + + $www_cmds = [ + 'laravel-echo-server client:add', + 'composer install -q', + 'bun install', + 'bun run build', + 'php artisan key:generate', + 'php artisan migrate --seed', + 'php artisan auto:email-blacklist-update', + 'php artisan test:email' + ]; + + foreach ($www_cmds as $cmd) { + $this->process([ + "su $web_user -s /bin/bash --command=\"cd $install_dir && $cmd\"" + ], true); + } + + $this->io->writeln(' '); + } + + protected function crons() + { + $this->io->writeln("\n\nSetting Up Crontabs"); + $this->seperator(); + + $install_dir = $this->config->os('install_dir'); + + $this->process([ + "(crontab -l ; echo \"* * * * * php $install_dir/artisan schedule:run >> /dev/null 2>&1\") | crontab -" + ]); + } + +} diff --git a/src/Installer/Utilities/RestartService.php b/src/Installer/Utilities/RestartService.php new file mode 100644 index 0000000..b011eda --- /dev/null +++ b/src/Installer/Utilities/RestartService.php @@ -0,0 +1,21 @@ +throwError( + "Null Argument supplied in handle method for RestartService::class. + + Expecting string value" + ); + } + + $this->salt->execute($target, 'service.restart', [$service]); + } +} \ No newline at end of file diff --git a/src/Resources/.env.stub b/src/Resources/.env.stub new file mode 100644 index 0000000..21086d1 --- /dev/null +++ b/src/Resources/.env.stub @@ -0,0 +1,49 @@ +APP_ENV=prod +APP_KEY= +APP_DEBUG=false +APP_URL={{PROTOCOL}}://{{FQDN}} +VITE_ECHO_ADDRESS={{PROTOCOL}}://{{FQDN}}:8443 + +LOG_CHANNEL=daily +LOG_LEVEL=info + +DB_CONNECTION={{DBDRIVER}} +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_DATABASE={{DB}} +DB_USERNAME={{DBUSER}} +DB_PASSWORD={{DBPASS}} + +BROADCAST_CONNECTION=redis +CACHE_STORE=redis +QUEUE_CONNECTION=redis + +SESSION_DRIVER=redis +SESSION_CONNECTION=session +SESSION_LIFETIME=120 +SESSION_SECURE_COOKIE=true + +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_DRIVER={{MAILDRIVER}} +MAIL_HOST={{MAILHOST}} +MAIL_PORT={{MAILPORT}} +MAIL_USERNAME={{MAILUSERNAME}} +MAIL_PASSWORD={{MAILPASSWORD}} +MAIL_ENCRYPTION=TLS +MAIL_FROM_NAME={{MAILFROMNAME}} +MAIL_FROM_ADDRESS={{OWNEREMAIL}} + +DEFAULT_OWNER_NAME={{OWNER}} +DEFAULT_OWNER_EMAIL={{OWNEREMAIL}} +DEFAULT_OWNER_PASSWORD={{OWNERPASSWORD}} + +TMDB_API_KEY={{TMDBAPIKEY}} +TWITCH_CLIENT_ID= +TWITCH_CLIENT_SECRET= + +TRACKER_KEY= +TRACKER_HOST=127.0.0.1 +TRACKER_PORT=6969 diff --git a/src/Resources/intro.stub b/src/Resources/intro.stub new file mode 100644 index 0000000..d0521f4 --- /dev/null +++ b/src/Resources/intro.stub @@ -0,0 +1,14 @@ + + __ __ _ __ ____ ______ _____ ____ ______ _ __ ______ __ _ __ _ + / / / // | / // _//_ __/|__ / / __ \ / ____/____ ____ ___ ____ ___ __ __ ____ (_)/ /_ __ __ / ____/____/ /(_)/ /_ (_)____ ____ + / / / // |/ / / / / / /_ < / / / /______ / / / __ \ / __ `__ \ / __ `__ \ / / / // __ \ / // __// / / /______ / __/ / __ // // __// // __ \ / __ \ +/ /_/ // /| /_/ / / / ___/ // /_/ //_____// /___ / /_/ // / / / / // / / / / // /_/ // / / // // /_ / /_/ //_____// /___ / /_/ // // /_ / // /_/ // / / / +\____//_/ |_//___/ /_/ /____//_____/ \____/ \____//_/ /_/ /_//_/ /_/ /_/ \__,_//_/ /_//_/ \__/ \__, / /_____/ \__,_//_/ \__//_/ \____//_/ /_/ + /____/ + + + *-----------------------------------------------* + | Copyright: 2017-2024 | + | Founder: HDVinnie | + | Maintainers: HDVinnie, Roardom & Community | + *-----------------------------------------------* \ No newline at end of file diff --git a/src/Resources/laravel-echo-server.stub b/src/Resources/laravel-echo-server.stub new file mode 100644 index 0000000..570bc51 --- /dev/null +++ b/src/Resources/laravel-echo-server.stub @@ -0,0 +1,27 @@ +{ + "authHost": "{{PROTOCOL}}://{{FQDN}}", + "authEndpoint": "/broadcasting/auth", + "clients": [], + "database": "redis", + "databaseConfig": { + "redis": {}, + "sqlite": { + "databasePath": "/database/laravel-echo-server.sqlite" + } + }, + "devMode": false, + "host": null, + "port": "{{PORT}}", + "protocol": "{{PROTOCOL}}", + "socketio": {}, + "sslCertPath": "/etc/letsencrypt/live/{{FQDN}}/cert.pem", + "sslKeyPath": "/etc/letsencrypt/live/{{FQDN}}/privkey.pem", + "sslCertChainPath": "/etc/letsencrypt/live/{{FQDN}}/fullchain.pem", + "sslPassphrase": "", + "apiOriginAllow": { + "allowCors": false, + "allowOrigin": "", + "allowMethods": "", + "allowHeaders": "" + } +} diff --git a/src/Resources/ubuntu/mysql/.my.cnf b/src/Resources/ubuntu/mysql/.my.cnf new file mode 100644 index 0000000..d7a0f57 --- /dev/null +++ b/src/Resources/ubuntu/mysql/.my.cnf @@ -0,0 +1,2 @@ +[client] +password={{PASSWORD}} diff --git a/src/Resources/ubuntu/mysql/my-large.cnf b/src/Resources/ubuntu/mysql/my-large.cnf new file mode 100644 index 0000000..f66e72f --- /dev/null +++ b/src/Resources/ubuntu/mysql/my-large.cnf @@ -0,0 +1,40 @@ +[client] +port=3306 +socket=/var/run/mysqld/mysqld.sock + +[mysqld_safe] +socket=/var/run/mysqld/mysqld.sock + +[mysqld] +user=mysql +pid-file=/var/run/mysqld/mysqld.pid +socket=/var/run/mysqld/mysqld.sock +port=3306 +basedir=/usr +datadir=/var/lib/mysql +tmpdir=/tmp +lc-messages-dir=/usr/share/mysql +log_error=/var/log/mysql/error.log + +symbolic-links=0 + +skip-external-locking +key_buffer_size = 256M +max_allowed_packet = 32M +table_open_cache = 256 +sort_buffer_size = 1M +read_buffer_size = 1M +read_rnd_buffer_size = 4M +myisam_sort_buffer_size = 64M +thread_cache_size = 8 + +#innodb_use_native_aio = 0 +innodb_file_per_table + +max_connections=200 +max_user_connections=50 +wait_timeout=10 +interactive_timeout=50 +long_query_time=5 + +!includedir /etc/mysql/conf.d/ \ No newline at end of file diff --git a/src/Resources/ubuntu/mysql/my-medium.cnf b/src/Resources/ubuntu/mysql/my-medium.cnf new file mode 100644 index 0000000..f51e5be --- /dev/null +++ b/src/Resources/ubuntu/mysql/my-medium.cnf @@ -0,0 +1,40 @@ +[client] +port=3306 +socket=/var/run/mysqld/mysqld.sock + +[mysqld_safe] +socket=/var/run/mysqld/mysqld.sock + +[mysqld] +user=mysql +pid-file=/var/run/mysqld/mysqld.pid +socket=/var/run/mysqld/mysqld.sock +port=3306 +basedir=/usr +datadir=/var/lib/mysql +tmpdir=/tmp +lc-messages-dir=/usr/share/mysql +log_error=/var/log/mysql/error.log + +symbolic-links=0 + +skip-external-locking +key_buffer_size = 16M +max_allowed_packet = 16M +table_open_cache = 64 +sort_buffer_size = 512K +net_buffer_length = 8K +read_buffer_size = 256K +read_rnd_buffer_size = 512K +myisam_sort_buffer_size = 8M + +#innodb_use_native_aio = 0 +innodb_file_per_table + +max_connections=70 +max_user_connections=30 +wait_timeout=10 +interactive_timeout=50 +long_query_time=5 + +!includedir /etc/mysql/conf.d/ \ No newline at end of file diff --git a/src/Resources/ubuntu/mysql/my-small.cnf b/src/Resources/ubuntu/mysql/my-small.cnf new file mode 100644 index 0000000..aea887c --- /dev/null +++ b/src/Resources/ubuntu/mysql/my-small.cnf @@ -0,0 +1,40 @@ +[client] +port=3306 +socket=/var/run/mysqld/mysqld.sock + +[mysqld_safe] +socket=/var/run/mysqld/mysqld.sock + +[mysqld] +user=mysql +pid-file=/var/run/mysqld/mysqld.pid +socket=/var/run/mysqld/mysqld.sock +port=3306 +basedir=/usr +datadir=/var/lib/mysql +tmpdir=/tmp +lc-messages-dir=/usr/share/mysql +log_error=/var/log/mysql/error.log + +symbolic-links=0 + +skip-external-locking +key_buffer_size = 16K +max_allowed_packet = 1M +table_open_cache = 4 +sort_buffer_size = 64K +read_buffer_size = 256K +read_rnd_buffer_size = 256K +net_buffer_length = 2K +thread_stack = 240K + +#innodb_use_native_aio = 0 +innodb_file_per_table + +max_connections=30 +max_user_connections=20 +wait_timeout=10 +interactive_timeout=50 +long_query_time=5 + +!includedir /etc/mysql/conf.d/ \ No newline at end of file diff --git a/src/Resources/ubuntu/nginx/default.site b/src/Resources/ubuntu/nginx/default.site new file mode 100644 index 0000000..9860934 --- /dev/null +++ b/src/Resources/ubuntu/nginx/default.site @@ -0,0 +1,37 @@ +server { + listen 80 default_server; + + root /var/www/html/public; + + index index.php; + + server_name {{FQDN}} www.{{FQDN}}; + + location ~* \.(jpg|jpeg|png|gif|ico|svg)$ { + expires 365d; + } + + location ~* \.(gif|png|jpg|jpeg|svg|css|js|ico)$ { + valid_referers none blocked {{FQDN}} www.{{FQDN}}; + if ($invalid_referer) { + return 403; + } + } + + location / { + try_files $uri $uri/ /index.php$is_args$args; + } + + location ~ \.php$ { + include snippets/fastcgi-php.conf; + fastcgi_pass unix:/var/run/php/{{FQDN}}.sock; + } + + location /index.php { + return 301 {{FQDN}}; + } + + location ~* ^.*(\.(?:git|svn|htaccess|github))$ { + return 403; + } +} diff --git a/src/Resources/ubuntu/php-fpm/php-fpm.conf b/src/Resources/ubuntu/php-fpm/php-fpm.conf new file mode 100644 index 0000000..7275ca5 --- /dev/null +++ b/src/Resources/ubuntu/php-fpm/php-fpm.conf @@ -0,0 +1,16 @@ +[{{FQDN}}] + + listen = /var/run/php/{{FQDN}}.sock + listen.backlog = 511 + listen.owner = {{WEBUSER}} + listen.group = {{WEBUSER}} + listen.mode=0660 + + user = {{WEBUSER}} + group = {{WEBUSER}} + + pm = ondemand + pm.max_children = 10 + pm.process_idle_timeout = 10 + pm.max_requests = 0 + chdir = / \ No newline at end of file diff --git a/src/Resources/ubuntu/supervisor/app.conf b/src/Resources/ubuntu/supervisor/app.conf new file mode 100644 index 0000000..3d1ee4a --- /dev/null +++ b/src/Resources/ubuntu/supervisor/app.conf @@ -0,0 +1,20 @@ +[program:unit3d-queue] +process_name=%(program_name)s_%(process_num)02d +command=php {{INSTALLDIR}}/artisan queue:work --tries=1 --max-jobs=1000 --max-time=3600 +startsecs = 0 +autostart=true +autorestart=true +stopasgroup=true +killasgroup=true +user=www-data +numprocs=4 +redirect_stderr=true +stopwaitsecs=3600 + +[program:unit3d-chat-server] +process_name=%(program_name)s_%(process_num)02d +command=/usr/bin/node /usr/bin/laravel-echo-server start --dir={{INSTALLDIR}} +autostart=true +autorestart=true +user={{WEBUSER}} +numprocs=1 diff --git a/src/Traits/ConsoleTools.php b/src/Traits/ConsoleTools.php new file mode 100644 index 0000000..cce3d64 --- /dev/null +++ b/src/Traits/ConsoleTools.php @@ -0,0 +1,88 @@ +io->writeln("[Warning] $msg\n"); + } + + /** + * Returns the installer error and exits the installer + * + * @param array|string $error + */ + protected function throwError($error = 'Unknown Error ...') + { + $this->io->writeln("$error"); + exit(1); + } + + protected function dump($var) + { + $this->io->writeln('---VAR DUMP---'); + var_dump($var); + $this->io->writeln('--------------'); + } + + /** + * Displays a the intro + */ + protected function displayIntro() + { + $stub = file_get_contents(__DIR__ . '/../Resources/intro.stub'); + + $this->io->text($stub); + } + + /** + * Writes a seperator + */ + protected function seperator() + { + $this->io->writeln(str_repeat('=', 80)); + } + + /** + * Formats a header + * + * @param string $text + */ + protected function head($text) + { + if ($text !== null) { + $this->io->writeln("\n" . str_repeat('=', 80)); + $this->io->writeln(' ' . $text . str_repeat(' ', (76 - strlen($text)))); + $this->io->writeln(str_repeat('=', 80) . "\n"); + } + } + + protected function success() + { + $this->io->writeln("\n[OK] Done!" . str_repeat(' ', 70) . ""); + } + + protected function question($question, $default = '') + { + do { + $answer = $this->io->ask($question, $default); + + $valid = ($answer !== '' && strpos($answer, ' ') === false); + + if (!$valid) { + $this->warning('Cannot be empty or contain spaces!'); + } + + } while (!$valid); + + return trim($answer); + } +} \ No newline at end of file diff --git a/tests/MasterBootstrapperTest.php b/tests/MasterBootstrapperTest.php new file mode 100644 index 0000000..3677e0d --- /dev/null +++ b/tests/MasterBootstrapperTest.php @@ -0,0 +1,14 @@ + /dev/null + export LC_ALL=en_US.UTF-8 + export LANG=en_US.UTF-8 + apt-get install -qq software-properties-common > /dev/null + + echo -e "$IGreen OK $Color_Off" +} + +# Adds PPA's +add_ppa() { + echo -e "\n$Cyan Adding PPA Repositories ... $Color_Off" + + for ppa in "$@"; do + add-apt-repository -y $ppa > /dev/null 2>&1 + check $? "Adding $ppa Failed!" + done + + echo -e "$IGreen OK $Color_Off" +} + +# Installs Environment Prerequisites +add_pkgs() { + # Update apt + echo -e "\n$Cyan Updating Packages ... $Color_Off" + + apt-get -qq update > /dev/null + check $? "Updating packages Failed!" + + echo -e "$IGreen OK $Color_Off" + + # PHP + echo -e "\n$Cyan Installing PHP ... $Color_Off" + + apt-get -qq install curl php-pear php8.3-common php8.3-cli php8.3-fpm php8.3-{redis,bcmath,curl,dev,gd,igbinary,intl,mbstring,mysql,opcache,readline,xml,zip} > /dev/null + check $? "Installing PHP Failed!" + + echo -e "$IGreen OK $Color_Off" + + # Redis + echo -e "\n$Cyan Installing Redis ... $Color_Off" + + curl -fsSL https://packages.redis.io/gpg | sudo gpg --yes --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg + echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list > /dev/null + apt-get -qq update > /dev/null + apt-get -qq install redis > /dev/null + + echo -e "$IGreen OK $Color_Off" + + # Symlink Redis and Enable + echo -e "\n$Cyan Symlink and Enabling Redis ... $Color_Off" + + systemctl -q enable --now redis-server + systemctl is-active --quiet redis-server && echo -e "$IGreen OK $Color_Off"||echo -e "$IRed NOK $Color_Off" + + # PHP Redis + echo -e "\n$Cyan Installing PHP Redis ... $Color_Off" + + printf "\n" | pecl install redis > /dev/null + + echo -e "$IGreen OK $Color_Off" + + # Update Dependencies + echo -e "\n$Cyan Updating Dependencies ... $Color_Off" + + apt-get -qq upgrade > /dev/null + + echo -e "$IGreen OK $Color_Off" + + # Bun + echo -e "\n$Cyan Installing Bun ... $Color_Off" + + apt-get -qq install unzip > /dev/null + curl -fsSL https://bun.sh/install | bash >/dev/null 2>&1 + mv /root/.bun/bin/bun /usr/local/bin/ + chmod a+x /usr/local/bin/bun + . ~/.bashrc + + echo -e "$IGreen OK $Color_Off" +} + +# Installs Composer +install_composer() { + echo -e "\n$Cyan Installing Composer ... $Color_Off" + + php -r "readfile('http://getcomposer.org/installer');" | sudo php -- --install-dir=/usr/bin/ --filename=composer > /dev/null + check $? "Installing Composer Failed!" + + echo -e "$IGreen OK $Color_Off" +} + +# Adds installer packages +installer_pkgs() { + echo -e "\n$Cyan Adding Installer Packages ... $Color_Off" + + composer install -qq > /dev/null 2>&1 + check $? "Adding Installer Packages Failed!" + + echo -e "$IGreen OK $Color_Off" +} + +# Checks the returned code +check() { + if [ $1 -ne 0 ]; then + echo -e "$Red Error: $2 \n Please try re-running the script via 'sudo ./install.sh' $Color_Off" + exit $1 + fi +} + +check_locale + +add_ppa ppa:ondrej/php + +add_pkgs + +install_composer + +installer_pkgs + +echo -e "\n$Purple Launching The Installer ... $Color_Off" +echo "=============================================" +php artisan install