From 1d23fba32825738a6b45a8b3c8ef934f13454e0a Mon Sep 17 00:00:00 2001 From: airclay Date: Sat, 18 Jan 2025 06:24:08 +0000 Subject: [PATCH] first --- README.md | 38 + artisan | 29 + box.json | 21 + composer.json | 22 + composer.lock | 2670 +++++++++++++++++ install.sh | 51 + phpunit.xml | 21 + src/BaseTestCase.php | 37 + src/Classes/Config.php | 180 ++ src/Classes/Process.php | 127 + src/Commands/InstallCommand.php | 107 + src/Configs/app.php | 53 + src/Configs/os.php | 32 + src/Helpers/helpers.php | 199 ++ src/Installer/BaseInstaller.php | 83 + src/Installer/Database/DatabaseSetup.php | 16 + src/Installer/Database/MySqlSetup.php | 81 + src/Installer/Nginx/NginxSetup.php | 44 + src/Installer/PHP/PhpSetup.php | 49 + src/Installer/Policies/AppKeyExists.php | 18 + src/Installer/Policies/AppNotInstalled.php | 13 + src/Installer/Policies/BasePolicy.php | 36 + .../Policies/DatabaseDriverKeyExists.php | 42 + .../Policies/DatabaseInstallersKeyExists.php | 49 + .../Policies/InstallDirKeyExists.php | 19 + src/Installer/Policies/InstallerPolicies.php | 31 + src/Installer/Policies/IsPhpVersionCompat.php | 14 + src/Installer/Policies/IsPrivilegedUser.php | 14 + .../Policies/PhpVersionKeyExists.php | 19 + src/Installer/Prerequisites/Prerequisites.php | 36 + src/Installer/Server/ServerSetup.php | 166 + src/Installer/UNIT3D/Unit3dSetup.php | 160 + src/Installer/Utilities/RestartService.php | 21 + src/Resources/.env.stub | 49 + src/Resources/intro.stub | 14 + src/Resources/laravel-echo-server.stub | 27 + src/Resources/ubuntu/mysql/.my.cnf | 2 + src/Resources/ubuntu/mysql/my-large.cnf | 40 + src/Resources/ubuntu/mysql/my-medium.cnf | 40 + src/Resources/ubuntu/mysql/my-small.cnf | 40 + src/Resources/ubuntu/nginx/default.site | 37 + src/Resources/ubuntu/php-fpm/php-fpm.conf | 16 + src/Resources/ubuntu/supervisor/app.conf | 20 + src/Traits/ConsoleTools.php | 88 + tests/MasterBootstrapperTest.php | 14 + tests/bootstrap.php | 7 + tools/colors.sh | 74 + ubuntu.sh | 138 + 48 files changed, 5104 insertions(+) create mode 100644 README.md create mode 100755 artisan create mode 100644 box.json create mode 100644 composer.json create mode 100644 composer.lock create mode 100755 install.sh create mode 100644 phpunit.xml create mode 100644 src/BaseTestCase.php create mode 100644 src/Classes/Config.php create mode 100644 src/Classes/Process.php create mode 100644 src/Commands/InstallCommand.php create mode 100644 src/Configs/app.php create mode 100644 src/Configs/os.php create mode 100644 src/Helpers/helpers.php create mode 100644 src/Installer/BaseInstaller.php create mode 100644 src/Installer/Database/DatabaseSetup.php create mode 100644 src/Installer/Database/MySqlSetup.php create mode 100644 src/Installer/Nginx/NginxSetup.php create mode 100644 src/Installer/PHP/PhpSetup.php create mode 100644 src/Installer/Policies/AppKeyExists.php create mode 100644 src/Installer/Policies/AppNotInstalled.php create mode 100644 src/Installer/Policies/BasePolicy.php create mode 100644 src/Installer/Policies/DatabaseDriverKeyExists.php create mode 100644 src/Installer/Policies/DatabaseInstallersKeyExists.php create mode 100644 src/Installer/Policies/InstallDirKeyExists.php create mode 100644 src/Installer/Policies/InstallerPolicies.php create mode 100644 src/Installer/Policies/IsPhpVersionCompat.php create mode 100644 src/Installer/Policies/IsPrivilegedUser.php create mode 100644 src/Installer/Policies/PhpVersionKeyExists.php create mode 100644 src/Installer/Prerequisites/Prerequisites.php create mode 100644 src/Installer/Server/ServerSetup.php create mode 100644 src/Installer/UNIT3D/Unit3dSetup.php create mode 100644 src/Installer/Utilities/RestartService.php create mode 100644 src/Resources/.env.stub create mode 100644 src/Resources/intro.stub create mode 100644 src/Resources/laravel-echo-server.stub create mode 100644 src/Resources/ubuntu/mysql/.my.cnf create mode 100644 src/Resources/ubuntu/mysql/my-large.cnf create mode 100644 src/Resources/ubuntu/mysql/my-medium.cnf create mode 100644 src/Resources/ubuntu/mysql/my-small.cnf create mode 100644 src/Resources/ubuntu/nginx/default.site create mode 100644 src/Resources/ubuntu/php-fpm/php-fpm.conf create mode 100644 src/Resources/ubuntu/supervisor/app.conf create mode 100644 src/Traits/ConsoleTools.php create mode 100644 tests/MasterBootstrapperTest.php create mode 100644 tests/bootstrap.php create mode 100644 tools/colors.sh create mode 100755 ubuntu.sh 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