diff --git a/.gitignore b/.gitignore index a5fc231b0de8b288828eaaf05307a93461b2c293..fb44e822a67d3920c99ab2af8cc0410107e9b053 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,7 @@ +.idea +compiled/* logs/*/*.log +vendor/* + +config.json diff --git a/compiled/.gitkeep b/compiled/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/composer.json b/composer.json new file mode 100644 index 0000000000000000000000000000000000000000..6bc6df8138996ab4ddee4aa33af24704446ac932 --- /dev/null +++ b/composer.json @@ -0,0 +1,24 @@ +{ + "name": "tleilax/studip-dockerized", + "type": "project", + "require": { + "symfony/console": "^6.4", + "symfony/yaml": "^6.4", + "symfony/var-dumper": "^6.4", + "symfony/process": "^6.4", + "romanpitak/nginx-config-processor": "^0.2.1", + "symfony/filesystem": "^6.4" + }, + "license": "gpl", + "authors": [ + { + "name": "Jan-Hendrik Willms", + "email": "tleilax@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Studip\\Dockerized\\":"lib/" + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000000000000000000000000000000000000..203f8ec44a6c318f1e1609d259a69c7b3d56c95d --- /dev/null +++ b/composer.lock @@ -0,0 +1,1063 @@ +{ + "_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": "f6dc7cf29c327525be62ebd3b59c2b90", + "packages": [ + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "romanpitak/nginx-config-processor", + "version": "v0.2.1", + "source": { + "type": "git", + "url": "https://github.com/romanpitak/Nginx-Config-Processor.git", + "reference": "0dfcbbda696c7baeed8f955e4831696d90e56758" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/romanpitak/Nginx-Config-Processor/zipball/0dfcbbda696c7baeed8f955e4831696d90e56758", + "reference": "0dfcbbda696c7baeed8f955e4831696d90e56758", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "codacy/coverage": "dev-master", + "codeclimate/php-test-reporter": "dev-master" + }, + "type": "library", + "autoload": { + "psr-4": { + "RomanPitak\\Nginx\\Config\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Piták", + "email": "roman@pitak.net", + "homepage": "http://pitak.net", + "role": "Developer" + } + ], + "description": "Nginx configuration files processor.", + "homepage": "https://github.com/romanpitak/Nginx-Config-Processor", + "keywords": [ + "conf", + "conf file", + "config", + "config file", + "configuration", + "configuration file", + "create", + "creator", + "manager", + "nginx", + "parser", + "php", + "processor" + ], + "support": { + "email": "roman@pitak.net", + "issues": "https://github.com/romanpitak/Nginx-Config-Processor/issues", + "source": "https://github.com/romanpitak/Nginx-Config-Processor" + }, + "time": "2016-01-31T19:44:23+00:00" + }, + { + "name": "symfony/console", + "version": "v6.4.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "a2708a5da5c87d1d0d52937bdeac625df659e11f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/a2708a5da5c87d1d0d52937bdeac625df659e11f", + "reference": "a2708a5da5c87d1d0d52937bdeac625df659e11f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0|^7.0" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" + }, + "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", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v6.4.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-03-29T19:07:53+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/filesystem", + "version": "v6.4.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "9919b5509ada52cc7f66f9a35c86a4a29955c9d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/9919b5509ada52cc7f66f9a35c86a4a29955c9d3", + "reference": "9919b5509ada52cc7f66f9a35c86a4a29955c9d3", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "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": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v6.4.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-03-21T19:36:20+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", + "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "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.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "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 intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "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 for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "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.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, + { + "name": "symfony/process", + "version": "v6.4.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "710e27879e9be3395de2b98da3f52a946039f297" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/710e27879e9be3395de2b98da3f52a946039f297", + "reference": "710e27879e9be3395de2b98da3f52a946039f297", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "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/v6.4.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-20T12:31:00+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.4.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "11bbf19a0fb7b36345861e85c5768844c552906e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/11bbf19a0fb7b36345861e85c5768844c552906e", + "reference": "11bbf19a0fb7b36345861e85c5768844c552906e", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.4.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": "2023-12-19T21:51:00+00:00" + }, + { + "name": "symfony/string", + "version": "v6.4.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "4e465a95bdc32f49cf4c7f07f751b843bbd6dcd9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/4e465a95bdc32f49cf4c7f07f751b843bbd6dcd9", + "reference": "4e465a95bdc32f49cf4c7f07f751b843bbd6dcd9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/intl": "^6.2|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "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 an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v6.4.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-02-01T13:16:41+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v6.4.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "95bd2706a97fb875185b51ecaa6112ec184233d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/95bd2706a97fb875185b51ecaa6112ec184233d4", + "reference": "95bd2706a97fb875185b51ecaa6112ec184233d4", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/console": "<5.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^6.3|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/uid": "^5.4|^6.0|^7.0", + "twig/twig": "^2.13|^3.0.4" + }, + "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/v6.4.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-03-19T11:56:30+00:00" + }, + { + "name": "symfony/yaml", + "version": "v6.4.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "d75715985f0f94f978e3a8fa42533e10db921b90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/d75715985f0f94f978e3a8fa42533e10db921b90", + "reference": "d75715985f0f94f978e3a8fa42533e10db921b90", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<5.4" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0|^7.0" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "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/v6.4.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-23T14:51:35+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "2.6.0" +} diff --git a/php/7.2-Dockerfile b/config/docker-files/7.2-Dockerfile similarity index 86% rename from php/7.2-Dockerfile rename to config/docker-files/7.2-Dockerfile index 0bb670ab0ffd1a5248530f5664952fe62192e7ef..96071ab9df751b33373af2b8c1f6cb0ac08c13e8 100644 --- a/php/7.2-Dockerfile +++ b/config/docker-files/7.2-Dockerfile @@ -5,7 +5,7 @@ RUN apt update && apt install -y --no-install-recommends \ default-mysql-client \ default-libmysqlclient-dev \ imagemagick ghostscript \ - libcurl4-openssl-dev zlib1g-dev \ + zlib1g-dev \ libjpeg-dev \ libpng-dev \ libpq-dev \ @@ -20,14 +20,14 @@ RUN apt update && apt install -y --no-install-recommends \ vim \ && rm -rf /var/lib/apt/lists/* -# Install php extensions -RUN docker-php-ext-configure gd --with-jpeg-dir --with-freetype-dir --with-webp-dir -RUN docker-php-ext-install -j$(nproc) gettext curl gd mbstring zip pdo pdo_mysql pdo_pgsql mysqli intl json soap - # Install de_DE locale RUN locale-gen de_DE.UTF-8 \ && update-locale +# Install php extensions +RUN docker-php-ext-configure gd --with-jpeg-dir --with-freetype-dir --with-webp-dir +RUN docker-php-ext-install -j$(nproc) gd gettext intl mysqli pdo_mysql pdo_pgsql soap zip + # Install Memcached & redis RUN pecl install memcached redis \ && docker-php-ext-enable memcached redis diff --git a/php/7.3-Dockerfile b/config/docker-files/7.3-Dockerfile similarity index 86% rename from php/7.3-Dockerfile rename to config/docker-files/7.3-Dockerfile index c6e13ff8780989478a570a907ec78fae92296955..3c1ff34e1780f68e51133a5b57aacd467319d1d2 100644 --- a/php/7.3-Dockerfile +++ b/config/docker-files/7.3-Dockerfile @@ -5,7 +5,7 @@ RUN apt update && apt install -y --no-install-recommends \ default-mysql-client \ default-libmysqlclient-dev \ imagemagick ghostscript \ - libcurl4-openssl-dev zlib1g-dev \ + zlib1g-dev \ libjpeg-dev \ libpng-dev \ libpq-dev \ @@ -20,14 +20,14 @@ RUN apt update && apt install -y --no-install-recommends \ vim \ && rm -rf /var/lib/apt/lists/* -# Install php extensions -RUN docker-php-ext-configure gd --with-jpeg-dir --with-freetype-dir --with-webp-dir -RUN docker-php-ext-install -j$(nproc) gettext curl gd mbstring zip pdo pdo_mysql pdo_pgsql mysqli intl json soap - # Install de_DE locale RUN locale-gen de_DE.UTF-8 \ && update-locale +# Install php extensions +RUN docker-php-ext-configure gd --with-jpeg-dir --with-freetype-dir --with-webp-dir +RUN docker-php-ext-install -j$(nproc) gd gettext intl mysqli pdo_mysql pdo_pgsql soap zip + # Install Memcached RUN pecl install memcached redis \ && docker-php-ext-enable memcached redis diff --git a/php/7.4-Dockerfile b/config/docker-files/7.4-Dockerfile similarity index 86% rename from php/7.4-Dockerfile rename to config/docker-files/7.4-Dockerfile index 4a74f8934bf5175d0dc38880aaec1122312a8279..fb3afe0a18b7dc4fe9d655b840feb0f35b0f27e7 100644 --- a/php/7.4-Dockerfile +++ b/config/docker-files/7.4-Dockerfile @@ -5,7 +5,7 @@ RUN apt update && apt install -y --no-install-recommends \ default-mysql-client \ default-libmysqlclient-dev \ imagemagick ghostscript \ - libcurl4-openssl-dev zlib1g-dev \ + zlib1g-dev \ libjpeg-dev \ libpng-dev \ libpq-dev \ @@ -20,14 +20,14 @@ RUN apt update && apt install -y --no-install-recommends \ vim \ && rm -rf /var/lib/apt/lists/* -# Install php extensions -RUN docker-php-ext-configure gd --with-jpeg --with-freetype --with-webp -RUN docker-php-ext-install -j$(nproc) gettext curl gd mbstring zip pdo pdo_mysql pdo_pgsql mysqli intl json soap - # Install de_DE locale RUN locale-gen de_DE.UTF-8 \ && update-locale +# Install php extensions +RUN docker-php-ext-configure gd --with-jpeg --with-freetype --with-webp +RUN docker-php-ext-install -j$(nproc) gd gettext intl pdo_mysql pdo_pgsql soap zip + # Install Memcached & redis RUN pecl install memcached redis \ && docker-php-ext-enable memcached redis diff --git a/php/8.0-Dockerfile b/config/docker-files/8.0-Dockerfile similarity index 86% rename from php/8.0-Dockerfile rename to config/docker-files/8.0-Dockerfile index 2f795f203fc26dc2825218b817ba13828bcf7127..9fd1fc6d2cb100b342bd3497528c5697a1fe9393 100644 --- a/php/8.0-Dockerfile +++ b/config/docker-files/8.0-Dockerfile @@ -5,7 +5,7 @@ RUN apt update && apt install -y --no-install-recommends \ default-mysql-client \ default-libmysqlclient-dev \ imagemagick ghostscript \ - libcurl4-openssl-dev zlib1g-dev \ + zlib1g-dev \ libjpeg-dev \ libpng-dev \ libpq-dev \ @@ -20,14 +20,14 @@ RUN apt update && apt install -y --no-install-recommends \ vim \ && rm -rf /var/lib/apt/lists/* -# Install php extensions -RUN docker-php-ext-configure gd --with-jpeg --with-freetype --with-webp -RUN docker-php-ext-install -j$(nproc) pdo gettext curl gd mbstring zip pdo_mysql pdo_pgsql mysqli intl soap - # Install de_DE locale RUN locale-gen de_DE.UTF-8 \ && update-locale +# Install php extensions +RUN docker-php-ext-configure gd --with-jpeg --with-freetype --with-webp +RUN docker-php-ext-install -j$(nproc) gd gettext intl mysqli pdo_mysql pdo_pgsql soap zip + # Install Memcached & redis RUN pecl install memcached redis \ && docker-php-ext-enable memcached redis diff --git a/php/8.1-Dockerfile b/config/docker-files/8.1-Dockerfile similarity index 86% rename from php/8.1-Dockerfile rename to config/docker-files/8.1-Dockerfile index 3bd5c8e382c90d43255d0ae739b1d23854561db4..b6eb7da4bc641ac1c3dfc8513548fb597edb2cf2 100644 --- a/php/8.1-Dockerfile +++ b/config/docker-files/8.1-Dockerfile @@ -5,7 +5,7 @@ RUN apt update && apt install -y --no-install-recommends \ default-mysql-client \ default-libmysqlclient-dev \ imagemagick ghostscript \ - libcurl4-openssl-dev zlib1g-dev \ + zlib1g-dev \ libjpeg-dev \ libpng-dev \ libpq-dev \ @@ -20,14 +20,14 @@ RUN apt update && apt install -y --no-install-recommends \ vim \ && rm -rf /var/lib/apt/lists/* -# Install php extensions -RUN docker-php-ext-configure gd --with-jpeg --with-freetype --with-webp -RUN docker-php-ext-install -j$(nproc) pdo gettext curl gd mbstring zip pdo_mysql pdo_pgsql mysqli intl soap - # Install de_DE locale RUN locale-gen de_DE.UTF-8 \ && update-locale +# Install php extensions +RUN docker-php-ext-configure gd --with-jpeg --with-freetype --with-webp +RUN docker-php-ext-install -j$(nproc) gd gettext intl mysqli pdo_mysql pdo_pgsql soap zip + # Install Memcached & redis RUN pecl install memcached redis \ && docker-php-ext-enable memcached redis diff --git a/php/8.2-Dockerfile b/config/docker-files/8.2-Dockerfile similarity index 86% rename from php/8.2-Dockerfile rename to config/docker-files/8.2-Dockerfile index 16639e43759ded3d1eca3253ba4f11be4102d3f4..69b9d252829f552c31724c8380873f23be21bfb3 100644 --- a/php/8.2-Dockerfile +++ b/config/docker-files/8.2-Dockerfile @@ -5,7 +5,7 @@ RUN apt update && apt install -y --no-install-recommends \ default-mysql-client \ default-libmysqlclient-dev \ imagemagick ghostscript \ - libcurl4-openssl-dev zlib1g-dev \ + zlib1g-dev \ libjpeg-dev \ libpng-dev \ libpq-dev \ @@ -20,14 +20,14 @@ RUN apt update && apt install -y --no-install-recommends \ vim \ && rm -rf /var/lib/apt/lists/* -# Install php extensions -RUN docker-php-ext-configure gd --with-jpeg --with-freetype --with-webp -RUN docker-php-ext-install -j$(nproc) pdo gettext curl gd mbstring zip pdo_mysql pdo_pgsql mysqli intl soap - # Install de_DE locale RUN locale-gen de_DE.UTF-8 \ && update-locale +# Install php extensions +RUN docker-php-ext-configure gd --with-jpeg --with-freetype --with-webp +RUN docker-php-ext-install -j$(nproc) gd gettext intl mysqli pdo_mysql pdo_pgsql soap zip + # Install Memcached & redis RUN pecl install memcached redis \ && docker-php-ext-enable memcached redis diff --git a/php/8.3-Dockerfile b/config/docker-files/8.3-Dockerfile similarity index 87% rename from php/8.3-Dockerfile rename to config/docker-files/8.3-Dockerfile index 56042356c5278a309996360bb6bf2d1849c97e19..7f8df20c05f599953ff26bcb685e654c0effe382 100644 --- a/php/8.3-Dockerfile +++ b/config/docker-files/8.3-Dockerfile @@ -6,7 +6,7 @@ RUN apt update \ default-mysql-client \ default-libmysqlclient-dev \ imagemagick ghostscript \ - libcurl4-openssl-dev zlib1g-dev \ + zlib1g-dev \ libjpeg-dev \ libpng-dev \ libpq-dev \ @@ -21,14 +21,14 @@ RUN apt update \ vim \ && rm -rf /var/lib/apt/lists/* -# Install php extensions -RUN docker-php-ext-configure gd --with-jpeg --with-freetype --with-webp -RUN docker-php-ext-install -j$(nproc) curl gd gettext intl mbstring mysqli pdo pdo_mysql pdo_pgsql soap zip - # Install de_DE locale RUN locale-gen de_DE.UTF-8 \ && update-locale +# Install php extensions +RUN docker-php-ext-configure gd --with-jpeg --with-freetype --with-webp +RUN docker-php-ext-install -j$(nproc) gd gettext intl mysqli pdo_mysql pdo_pgsql soap zip + # Install Memcached and redis RUN pecl install memcached redis \ && docker-php-ext-enable memcached redis diff --git a/config/nginx-php.conf b/config/nginx-php.conf deleted file mode 100644 index f1329d4fe6e60cbb0191a3ae715140ff96e37428..0000000000000000000000000000000000000000 --- a/config/nginx-php.conf +++ /dev/null @@ -1,11 +0,0 @@ -location ~ \.php(?:$|/) { - include fastcgi_params; - fastcgi_split_path_info ^(.+?\.php)(/.*)$; - fastcgi_intercept_errors on; - fastcgi_pass $fastcgi_backend; - fastcgi_index index.php; - fastcgi_param SERVER_NAME $http_host; - fastcgi_param DOCUMENT_ROOT $site_document_root; - fastcgi_param PATH_INFO $fastcgi_path_info; - fastcgi_param SCRIPT_FILENAME $request_filename; -} diff --git a/config/nginx-sites.conf b/config/nginx-sites.conf deleted file mode 100644 index f6784aa5d1a53c672412ae4e729d22e215fc8d15..0000000000000000000000000000000000000000 --- a/config/nginx-sites.conf +++ /dev/null @@ -1,78 +0,0 @@ -location / { - include nginx-php.conf; -} - -location ^~ /trunk { - alias /var/www/html/studip/trunk/public; - index index.php index.html index.html; - - set $site_document_root /var/www/html/studip/trunk/public; - include nginx-php.conf; -} - -location ^~ /5.0 { - alias /var/www/html/studip/5.0/public; - index index.php index.html index.html; - - set $site_document_root /var/www/html/studip/5.0/public; - include nginx-php.conf; -} -location ^~ /5.1 { - alias /var/www/html/studip/5.1/public; - index index.php index.html index.html; - - set $site_document_root /var/www/html/studip/5.1/public; - include nginx-php.conf; -} -location ^~ /5.2 { - alias /var/www/html/studip/5.2/public; - index index.php index.html index.html; - - set $site_document_root /var/www/html/studip/5.2/public; - include nginx-php.conf; -} -location ^~ /5.3 { - alias /var/www/html/studip/5.3/public; - index index.php index.html index.html; - - set $site_document_root /var/www/html/studip/5.3/public; - include nginx-php.conf; -} -location ^~ /5.4 { - alias /var/www/html/studip/5.4/public; - index index.php index.html index.html; - - set $site_document_root /var/www/html/studip/5.4/public; - include nginx-php.conf; -} -location ^~ /5.5 { - alias /var/www/html/studip/5.5/public; - index index.php index.html index.html; - - set $site_document_root /var/www/html/studip/5.5/public; - include nginx-php.conf; -} - -location ^~ /uol-5.4 { - alias /var/www/html/studip/uol/5.4/public; - index index.php index.html index.html; - - set $site_document_root /var/www/html/studip/uol/5.4/public; - include nginx-php.conf; -} - -location ^~ /uol-5.1 { - alias /var/www/html/studip/uol/5.1/public; - index index.php index.html index.html; - - set $site_document_root /var/www/html/studip/uol/5.1/public; - include nginx-php.conf; -} - -location ^~ /peoe-4.6 { - alias /var/www/html/studip/uol/peoe-4.6/public; - index index.php index.html index.html; - - set $site_document_root /var/www/html/studip/uol/peoe-4.6/public; - include nginx-php.conf; -} diff --git a/config/nginx.conf b/config/nginx.conf deleted file mode 100644 index d91e17ab14b8ea520ad069f111f759192df20583..0000000000000000000000000000000000000000 --- a/config/nginx.conf +++ /dev/null @@ -1,91 +0,0 @@ -upstream php74_backend { - server php74:9000; -} -upstream php80_backend { - server php80:9000; -} -upstream php81_backend { - server php81:9000; -} -upstream php82_backend { - server php82:9000; -} -upstream php83_backend { - server php83:9000; -} -upstream php72_backend { - server php72:9000; -} -upstream php73_backend { - server php73:9000; -} - -server { - listen 80 default_server; - server_name _; - root /var/www/html; - - set $fastcgi_backend php74_backend; - - include sites.conf; -} - -server { - listen 81 default_server; - server_name _; - root /var/www/html; - - set $fastcgi_backend php80_backend; - - include sites.conf; -} - -server { - listen 82 default_server; - server_name _; - root /var/www/html; - - set $fastcgi_backend php81_backend; - - include sites.conf; -} - -server { - listen 83 default_server; - server_name _; - root /var/www/html; - - set $fastcgi_backend php82_backend; - - include sites.conf; -} - -server { - listen 84 default_server; - server_name _; - root /var/www/html; - - set $fastcgi_backend php83_backend; - - include sites.conf; -} - -server { - listen 88 default_server; - server_name _; - root /var/www/html; - - set $fastcgi_backend php72_backend; - - include sites.conf; -} - -server { - listen 89 default_server; - server_name _; - root /var/www/html; - - set $fastcgi_backend php73_backend; - - include sites.conf; -} diff --git a/data/index.php b/data/index.php deleted file mode 100644 index 61ace196d4129411be0800919d883e132b0bf075..0000000000000000000000000000000000000000 --- a/data/index.php +++ /dev/null @@ -1,2 +0,0 @@ -<?php -phpinfo(); diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index f53ac7aa6cd8423c0a95eff6527f24ab093436b3..0000000000000000000000000000000000000000 --- a/docker-compose.yml +++ /dev/null @@ -1,75 +0,0 @@ -version: "3" - -services: - nginx: - image: nginx:alpine - ports: - - "8074:80" - - "8080:81" - - "8081:82" - - "8082:83" - - "8083:84" - - "8072:88" - - "8073:89" - networks: - - code-network - depends_on: - - redis-server - - memcached-server - volumes: - - ./config/nginx.conf:/etc/nginx/conf.d/default.conf - - ./config/nginx-sites.conf:/etc/nginx/sites.conf - - ./config/nginx-php.conf:/etc/nginx/nginx-php.conf - - ./data:/var/www/html - - ~/Code/studip:/var/www/html/studip:ro - - ./logs/nginx:/var/log/nginx/ - redis-server: - image: redis:latest - networks: - - code-network - memcached-server: - image: memcached:latest - networks: - - code-network - php74: &base-php - build: - context: . - dockerfile: php/7.4-Dockerfile - environment: - - MYSQL_HOST=host.docker.internal - networks: - - code-network - volumes: - - ./config/php-fpm.conf:/usr/local/etc/php-fpm.d/zz-config.conf - - ./data:/var/www/html - - ~/Code/studip:/var/www/html/studip - - ./logs/php/access.log:/var/log/php-access.log - - ./logs/php/error.log:/var/log/php-error.log - php80: - <<: *base-php - build: - dockerfile: php/8.0-Dockerfile - php81: - <<: *base-php - build: - dockerfile: php/8.1-Dockerfile - php82: - <<: *base-php - build: - dockerfile: php/8.2-Dockerfile - php83: - <<: *base-php - build: - dockerfile: php/8.3-Dockerfile - php72: - <<: *base-php - build: - dockerfile: php/7.2-Dockerfile - php73: - <<: *base-php - build: - dockerfile: php/7.3-Dockerfile - -networks: - code-network: - driver: bridge diff --git a/generate.sh b/generate.sh deleted file mode 100755 index 20da5cbdd7f582dd64a561622a4875cb83e35bb9..0000000000000000000000000000000000000000 --- a/generate.sh +++ /dev/null @@ -1 +0,0 @@ -docker compose up --build diff --git a/lib/Commands/Compile.php b/lib/Commands/Compile.php new file mode 100644 index 0000000000000000000000000000000000000000..3ead649497207bf892cb6d5dc47a4b48a5680ea9 --- /dev/null +++ b/lib/Commands/Compile.php @@ -0,0 +1,233 @@ +<?php +namespace Studip\Dockerized\Commands; + +use Studip\Dockerized\Config; +use Studip\Dockerized\Creators\DockerComposeConfiguration; +use Studip\Dockerized\Creators\NginxConfiguration; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +final class Compile extends \Symfony\Component\Console\Command\Command +{ + protected function configure() + { + $this->setName('compile'); + $this->setDescription('Compiles the configuration files'); + $this->addOption( + 'path', ' + p', + InputOption::VALUE_OPTIONAL, + 'Path to store the compiled files', + realpath(__DIR__ . '/../../compiled') + ); + $this->addOption('nginx', null, InputOption::VALUE_NEGATABLE, 'Compile nginx configuration', true); + $this->addOption('docker', null, InputOption::VALUE_NEGATABLE, 'Compile docker configuration', true); + } + + protected function initialize(InputInterface $input, OutputInterface $output) + { + if (!file_exists($input->getOption('path')) && !mkdir($input->getOption('path'))) { + $output->writeln('<error>Could not create compile directory</error>'); + return Command::FAILURE; + } + + if (!is_dir($input->getOption('path'))) { + $output->writeln('<error>Given compile directory is invalid (not a directory)</error>'); + return Command::FAILURE; + } + + if (!is_writable($input->getOption('path'))) { + $output->writeln('<error>Given compile directory is invalid (not writable)</error>'); + return Command::FAILURE; + } + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $compiledPath = realpath($input->getOption('path')); + $io = new SymfonyStyle($input, $output); + + $writeConfigFile = function (string $filename, string $content) use ($compiledPath, $io): void { + file_put_contents( + "{$compiledPath}/{$filename}", + $content + ); + $io->success('Config file ' . $filename . ' written'); + }; + + if ($input->getOption('nginx')) { + $writeConfigFile( + 'nginx.conf', + $this->createNginxConfiguration() + ); + } + + if ($input->getOption('docker')) { + $writeConfigFile( + 'docker-compose.yml', + $this->createDockerComposerConfiguration( + realpath(__DIR__ . '/../..'), + $compiledPath + ) + ); + } + + return Command::SUCCESS; + } + + private function getPHPVersionIndex(string $version, string $prefix = ''): string + { + return $prefix . preg_replace('/\D/', '', $version); + } + + public function createDockerComposerConfiguration(string $cwd, string $compiledPath): string + { + $creator = new DockerComposeConfiguration('studip-dockerized'); + + // Declare volumes + $volumes = [ + [realpath(__DIR__ . '/../../web'), '/var/www/html/studip-dockerized-config.app', 'ro'], + [realpath(__DIR__ . '/../../config.json'), '/var/www/html/studip-dockerized-config.json', 'ro'], + ...array_map( + fn(string $path, array $definition) => [$definition['source'], '/var/www/html/' . $path], + array_keys(Config::getInstance()->get('sites') ?? []), + array_values(Config::getInstance()->get('sites') ?? []) + ) + ]; + + // Nginx + $creator->addService('nginx', 'nginx:alpine', [ + 'networks' => ['code-network'], + 'depends_on' => ['redis-server', 'memcached-server'], + 'ports' => array_map( + function ($port) { + return "{$port}:{$port}"; + }, + array_values(Config::getInstance()->get('php')) + ), + ]); + + $creator->addServiceVolume( + 'nginx', + "{$compiledPath}/nginx.conf", + '/etc/nginx/conf.d/default.conf', + 'ro' + ); + $creator->addServiceVolume('nginx', realpath("{$cwd}/logs/nginx"), '/var/log/nginx'); + + + foreach ($volumes as $volume) { + $creator->addServiceVolume('nginx', ...$volume); + } + + // PHP + foreach (Config::getInstance()->get('php') as $version => $port) { + $index = $this->getPHPVersionIndex($version, 'php'); + + $creator->addService($index, null, [ + 'build' => [ + 'context' => '.', + 'dockerfile' => "{$cwd}/config/docker-files/{$version}-Dockerfile", + ], + 'environment' => [ + 'MYSQL_HOST' => 'host.docker.internal', + ], + 'networks' => ['code-network'], + ]); + + $creator->addServiceVolume( + $index, + "{$cwd}/config/php-fpm.conf", + '/usr/local/etc/php-fpm.d/zz-config.conf', + 'ro' + ); + $creator->addServiceVolume( + $index, + "{$cwd}/logs/php", + '/var/log/' + ); + + foreach ($volumes as $volume) { + $creator->addServiceVolume($index, ...$volume); + } + } + + // Other needed images + $creator->addService('memcached-server', 'memcached:latest', [ + 'networks' => ['code-network'], + ]); + + $creator->addService('redis-server', 'redis:latest', [ + 'networks' => ['code-network'], + ]); + + $creator->addNetwork('code-network', [ + 'driver' => 'bridge', + ]); + + + return $creator->dump(); + } + + public function createNginxConfiguration(): string + { + $creator = new NginxConfiguration(); + + // Register event handler to add the nginx-php configuration to each location + $creator->on(NginxConfiguration::EVENT_LOCATION_ADD_BEFORE, function ($location, &$config) use ($creator) { + $config[] = $creator->write('location ~ \.php(?:$|/)', [ + 'include fastcgi_params', + 'fastcgi_split_path_info ^(.+?\.php)(/.*)$', + 'fastcgi_intercept_errors on', + 'fastcgi_pass $fastcgi_backend', + 'fastcgi_index index.php', + 'fastcgi_param SERVER_NAME $http_host', + 'fastcgi_param DOCUMENT_ROOT $site_document_root', + 'fastcgi_param PATH_INFO $fastcgi_path_info', + 'fastcgi_param SCRIPT_FILENAME $request_filename', + ]); + }); + + // Add default location + $creator->addLocation('/sites', [ + 'alias /var/www/html/studip-dockerized-config.app', + 'index index.php index.html index.html', + 'set $site_document_root /var/www/html/studip-dockerized-config.app', + ]); + + // Add configured locations + foreach (Config::getInstance()->get('sites') ?? [] as $path => $definition) { + $p = '/var/www/html/' . $path; + if (isset($definition['mount'])) { + $p .= '/' . $definition['mount']; + } + + $creator->addLocation($path, [ + 'alias ' . $p, + 'index index.php index.html index.html', + 'set $site_document_root ' . $p, + ]); + } + + // Add upstreams and servers + foreach (Config::getInstance()->get('php') as $version => $port) { + $index = $this->getPHPVersionIndex($version, 'php'); + + $creator->addUpstream("{$index}_backend", [ + "server {$index}:9000" + ]); + + $creator->addServer([ + 'listen ' . $port . ' default_server', + 'server_name _', + 'root /var/www/html', + 'set $fastcgi_backend ' . $index . '_backend', + ]); + } + + return $creator->dump(); + } +} diff --git a/lib/Commands/Docker/Build.php b/lib/Commands/Docker/Build.php new file mode 100644 index 0000000000000000000000000000000000000000..080e444e727ffaa7a7c9d2586374464de75f47e7 --- /dev/null +++ b/lib/Commands/Docker/Build.php @@ -0,0 +1,17 @@ +<?php +namespace Studip\Dockerized\Commands\Docker; + +use Studip\Dockerized\DockerComposeCommand; + +final class Build extends DockerComposeCommand +{ + protected function configure() + { + $this->setName('docker:build'); + $this->setDescription('Build containers'); + } + protected function getDockerComposeCommand(): array + { + return ['up', '--no-start']; + } +} \ No newline at end of file diff --git a/lib/Commands/Docker/Start.php b/lib/Commands/Docker/Start.php new file mode 100644 index 0000000000000000000000000000000000000000..601769ed52c63f6aac6aa2b1908adc2f44855b1e --- /dev/null +++ b/lib/Commands/Docker/Start.php @@ -0,0 +1,18 @@ +<?php +namespace Studip\Dockerized\Commands\Docker; + +use Studip\Dockerized\DockerComposeCommand; + +final class Start extends DockerComposeCommand +{ + protected function configure() + { + $this->setName('docker:start'); + $this->setDescription('Starts docker'); + } + + protected function getDockerComposeCommand(): array + { + return ['start']; + } +} \ No newline at end of file diff --git a/lib/Commands/Docker/Stop.php b/lib/Commands/Docker/Stop.php new file mode 100644 index 0000000000000000000000000000000000000000..e81d4105884965833a478aa4837d1818bd47d40b --- /dev/null +++ b/lib/Commands/Docker/Stop.php @@ -0,0 +1,18 @@ +<?php +namespace Studip\Dockerized\Commands\Docker; + +use Studip\Dockerized\DockerComposeCommand; + +final class Stop extends DockerComposeCommand +{ + protected function configure() + { + $this->setName('docker:stop'); + $this->setDescription('Stops docker'); + } + + protected function getDockerComposeCommand(): array + { + return ['stop']; + } +} \ No newline at end of file diff --git a/lib/Commands/Init.php b/lib/Commands/Init.php new file mode 100644 index 0000000000000000000000000000000000000000..c19251078cedf3d58850df8b2fad809b8bcbb9e3 --- /dev/null +++ b/lib/Commands/Init.php @@ -0,0 +1,45 @@ +<?php +namespace Studip\Dockerized\Commands; + +use Studip\Dockerized\Config; +use Studip\Dockerized\SupportedPHPVersions; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +final class Init extends Command +{ + protected function configure() + { + $this->setName('init'); + $this->setDescription('Inits studip-dockerized'); + + $this->addOption('force', 'f', InputOption::VALUE_NONE, 'Force creation of config file'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + + if (file_exists(CONFIG_FILE) && !$input->getOption('force')) { + $io->error('Config file already exists.'); + return Command::FAILURE; + } + + $config = Config::create(CONFIG_FILE, false); + $config->set('php', array_combine( + SupportedPHPVersions::getDefaultVersions(), + array_map( + fn(string $version): int => SupportedPHPVersions::mapPort($version), + SupportedPHPVersions::getDefaultVersions() + ) + )); + $config->store(); + + $io->success('Config file has been created'); + + return Command::SUCCESS; + } +} \ No newline at end of file diff --git a/lib/Commands/PHP/Disable.php b/lib/Commands/PHP/Disable.php new file mode 100644 index 0000000000000000000000000000000000000000..cf91e12a2e4775fc0722b2ecb8fde30d42c021e5 --- /dev/null +++ b/lib/Commands/PHP/Disable.php @@ -0,0 +1,55 @@ +<?php +namespace Studip\Dockerized\Commands\PHP; + +use Studip\Dockerized\Config; +use Studip\Dockerized\SupportedPHPVersions; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +final class Disable extends Command +{ + protected function configure() + { + $this->setName('php:disable'); + $this->setDescription('Disable PHP version (needs recompile)'); + $this->addArgument( + 'version', + InputArgument::REQUIRED | InputArgument::IS_ARRAY, + 'PHP version(s) that should be disabled', + null, + SupportedPHPVersions::getVersions() + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $config = Config::getInstance(); + $io = new SymfonyStyle($input, $output); + + $disabled = 0; + foreach ($input->getArgument('version') as $version) { + if (!isset($config->get('php')[$version])) { + $io->error("PHP version {$version} is not enabled"); + continue; + } + + $php = $config->get('php'); + unset($php[$version]); + $config->set('php', $php); + + $io->success("PHP version {$version} has been disabled"); + $disabled += 1; + } + + if ($disabled === 0) { + return Command::FAILURE; + } + + $config->store(); + + return Command::SUCCESS; + } +} diff --git a/lib/Commands/PHP/Enable.php b/lib/Commands/PHP/Enable.php new file mode 100644 index 0000000000000000000000000000000000000000..67eac9b3cbf01cebecba474269f1898a0ad29d0e --- /dev/null +++ b/lib/Commands/PHP/Enable.php @@ -0,0 +1,57 @@ +<?php +namespace Studip\Dockerized\Commands\PHP; + +use Studip\Dockerized\Config; +use Studip\Dockerized\SupportedPHPVersions; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +final class Enable extends Command +{ + protected function configure() + { + $this->setName('php:enable'); + $this->setDescription('Enable PHP version (needs recompile)'); + $this->addArgument( + 'version', + InputArgument::REQUIRED | InputArgument::IS_ARRAY, + 'PHP version(s) that should be enabled', + null, + SupportedPHPVersions::getVersions() + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $config = Config::getInstance(); + $io = new SymfonyStyle($input, $output); + + $enabled = 0; + foreach ($input->getArgument('version') as $version) { + if (isset($config->get('php')[$version])) { + $io->error("PHP version {$version} is already enabled"); + continue; + } + + $port = SupportedPHPVersions::mapPort($version); + + $php = $config->get('php'); + $php[$version] = $port; + $config->set('php', $php); + + $io->success("PHP version {$version} has been enabled on port {$port}"); + $enabled += 1; + } + + if ($enabled === 0) { + return Command::FAILURE; + } + + $config->store(); + + return Command::SUCCESS; + } +} diff --git a/lib/Commands/PHP/Port.php b/lib/Commands/PHP/Port.php new file mode 100644 index 0000000000000000000000000000000000000000..18cdf7038ad5074bf195d9468bae7a4bfe772548 --- /dev/null +++ b/lib/Commands/PHP/Port.php @@ -0,0 +1,53 @@ +<?php +namespace Studip\Dockerized\Commands\PHP; + +use Studip\Dockerized\Config; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +final class Port extends Command +{ + protected function configure() + { + $this->setName('php:port'); + $this->setDescription('Change port for PHP version (needs recompile)'); + $this->addArgument( + 'version', + InputArgument::REQUIRED, + 'PHP version', + null, + array_keys(Config::getInstance()->get('php')) + ); + $this->addArgument( + 'port', + InputArgument::REQUIRED, + 'Port to use' + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $version = $input->getArgument('version'); + $port = $input->getArgument('port'); + + $config = Config::getInstance(); + $io = new SymfonyStyle($input, $output); + + if (!isset($config->get('php')[$version])) { + $io->error("PHP version {$version} is not enabled"); + return Command::FAILURE; + } + + $php = $config->get('php'); + $php[$version] = (int) $port; + $config->set('php', $php); + $config->store(); + + $io->success("Port of PHP version {$version} was changed to {$port}"); + + return Command::SUCCESS; + } +} \ No newline at end of file diff --git a/lib/Commands/Sites/Add.php b/lib/Commands/Sites/Add.php new file mode 100644 index 0000000000000000000000000000000000000000..0bbd870986926c90afef1b6b4f6dd3a779209f1b --- /dev/null +++ b/lib/Commands/Sites/Add.php @@ -0,0 +1,122 @@ +<?php +namespace Studip\Dockerized\Commands\Sites; + +use Exception; +use Studip\Dockerized\Config; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Filesystem\Path; + +final class Add extends Command +{ + protected function configure() + { + $this->setName('site:add'); + $this->setDescription('Adds a site (needs recompile)'); + $this->addArgument('webpath', InputArgument::OPTIONAL, 'The web path', ''); + $this->addArgument('localpath', InputArgument::OPTIONAL, 'The local path', ''); + $this->addArgument('mount', InputArgument::OPTIONAL, 'The mounted folder', null); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + + $question = $this->getWebPathQuestion(); + $webPath = $input->hasArgument('webpath') + ? $question->getValidator()($input->getArgument('webpath')) + : $io->askQuestion($question); + + $question = $this->getLocalPathQuestion(); + $localPath = $input->hasArgument('webpath') + ? $question->getValidator()($input->getArgument('localpath')) + : $io->askQuestion($question); + + $mount = $input->hasArgument('webpath') + ? $input->getArgument('mount') + : $io->ask('Mounted folder:'); + + $this->addSiteToConfig($webPath, $localPath, $mount); + + $io->success('The site "' . $webPath . '" was added.'); + + return Command::SUCCESS; + } + + private function getWebPathQuestion(): Question + { + $question = new Question('Web path'); + $question->setValidator(function (string $webPath): string { + $webPath = trim($webPath); + + if (!$webPath) { + throw new Exception('Web path cannot be empty'); + } + + if (array_key_exists($webPath, Config::getInstance()->get('sites') ?? [])) { + throw new Exception('Site "' . $webPath . '" already exists.'); + } + + return $webPath; + }); + return $question; + } + + private function getLocalPathQuestion(): Question + { + $question = new Question('Local path'); + $question->setValidator(function (string $localPath): string { + $localPath = trim($localPath); + $localPath = Path::canonicalize($localPath); + + if (!file_exists($localPath)) { + throw new Exception('Local path does not exist'); + } + + if (!is_readable($localPath)) { + throw new Exception('Local path is not readable.'); + } + + return $localPath; + + }); + $question->setAutocompleterCallback(function (string $input): array { + // Strip any characters from the last slash to the end of the string + // to keep only the last directory and generate suggestions for it + $inputPath = preg_replace('%(/|^)[^/]*$%', '$1', $input); + $inputPath = '' === $inputPath ? getcwd() : $inputPath; + + if ( + !file_exists(Path::canonicalize($inputPath)) + || !is_readable(Path::canonicalize($inputPath)) + ) { + return []; + } + + // CAUTION - this example code allows unrestricted access to the + // entire filesystem. In real applications, restrict the directories + // where files and dirs can be found + $foundFilesAndDirs = @scandir(Path::canonicalize($inputPath)) ?: []; + + return array_map(function (string $dirOrFile) use ($inputPath): string { + return $inputPath . $dirOrFile; + }, $foundFilesAndDirs); + }); + return $question; + } + + public function addSiteToConfig(mixed $webPath, string $source, ?string $mount): void + { + $config = Config::getInstance(); + $sites = $config->get('sites') ?? []; + $sites[$webPath] = array_filter(compact('source', 'mount')); + ksort($sites); + $config->set('sites', $sites); + + $config->store(); + } +} \ No newline at end of file diff --git a/lib/Commands/Sites/Remove.php b/lib/Commands/Sites/Remove.php new file mode 100644 index 0000000000000000000000000000000000000000..c435b62fef59a52793cfca3cd671773947d67480 --- /dev/null +++ b/lib/Commands/Sites/Remove.php @@ -0,0 +1,49 @@ +<?php + +namespace Studip\Dockerized\Commands\Sites; + +use Studip\Dockerized\Config; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +final class Remove extends Command +{ + protected function configure() + { + $this->setName('site:remove'); + $this->setDescription('Remove a site (needs recompile)'); + $this->addArgument('webpath', InputArgument::REQUIRED, 'The web path'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + + $webPath = $input->getArgument('webpath'); + + $config = Config::getInstance(); + $sites = $config->get('sites') ?? []; + + if (!array_key_exists($webPath, $sites)) { + $io->error("The site '{$webPath}' does not exist."); + return Command::FAILURE; + } + + unset($sites[$webPath]); + if (count($sites) > 0) { + ksort($sites); + $config->set('sites', $sites); + } else { + $config->delete('sites'); + } + + $config->store(); + + $io->success('Site ' . $webPath . ' has been removed.'); + + return Command::SUCCESS; + } +} \ No newline at end of file diff --git a/lib/Commands/Sites/Show.php b/lib/Commands/Sites/Show.php new file mode 100644 index 0000000000000000000000000000000000000000..ab081e8458e249fa87f3ab9cb05652d3011d8280 --- /dev/null +++ b/lib/Commands/Sites/Show.php @@ -0,0 +1,33 @@ +<?php +namespace Studip\Dockerized\Commands\Sites; + +use Studip\Dockerized\Config; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +final class Show extends Command +{ + protected function configure() + { + $this->setName('site:list'); + $this->setDescription('List all sites'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $table = new Table($output); + //$table->setStyle('compact'); + + $table->setHeaders(['Web', 'Local', 'Mount']); + + foreach (Config::getInstance()->get('sites') ?? [] as $key => $site) { + $table->addRow([$key, $site['source'], $site['mount'] ?? '-']); + }; + + $table->render(); + + return Command::SUCCESS; + } +} \ No newline at end of file diff --git a/lib/Config.php b/lib/Config.php new file mode 100644 index 0000000000000000000000000000000000000000..147ed44b3ae8ba6bcc0437092d8eae03b8fd61ab --- /dev/null +++ b/lib/Config.php @@ -0,0 +1,84 @@ +<?php +namespace Studip\Dockerized; + +final class Config +{ + private static ?Config $instance = null; + + public static function load(string $filename): void + { + self::$instance = self::create($filename); + } + + public static function create(string $filename, bool $initialReset = true): Config + { + return new self($filename, $initialReset); + } + + public static function getInstance(): Config + { + if (!isset(self::$instance)) { + throw new \Exception('Config has not been loaded'); + } + return self::$instance; + } + + private array $data = []; + private string $filename; + + private function __construct(string $filename, bool $initialReset = true) + { + $this->filename = $filename; + + if ($initialReset) { + $this->reset(); + } + } + + public function get(string $key): mixed + { + return $this->data[$key] ?? null; + } + + public function set(string $key, mixed $value): void + { + $this->data[$key] = $value; + } + + public function has(string $key): bool + { + return array_key_exists($key, $this->data); + } + + public function delete(string $key): void + { + unset($this->data[$key]); + } + + public function dump(): string + { + return json_encode($this->data, JSON_PRETTY_PRINT); + } + + public function reset(): void + { + if (!file_exists($this->filename)) { + throw new \Exception("Config file '{$this->filename}' does not exist"); + } + + if (!is_readable($this->filename)) { + throw new \Exception("Config file '{$this->filename}' is not readable"); + } + + $this->data = json_decode(file_get_contents($this->filename), true); + } + + public function store(): void + { + if (!is_writable($this->filename)) { + throw new \Exception("Config file '{$this->filename}' is not writable"); + } + + file_put_contents($this->filename, $this->dump()); + } +} \ No newline at end of file diff --git a/lib/Creators/DockerComposeConfiguration.php b/lib/Creators/DockerComposeConfiguration.php new file mode 100644 index 0000000000000000000000000000000000000000..0c494f312a4fbddbdfe0bbd9a1bd927851d0feb7 --- /dev/null +++ b/lib/Creators/DockerComposeConfiguration.php @@ -0,0 +1,70 @@ +<?php +namespace Studip\Dockerized\Creators; + +use Symfony\Component\Yaml\Yaml; + +final class DockerComposeConfiguration +{ + private array $configuration = []; + private string $cwd; + + public function __construct(string $cwd, string $name = '') + { + $this->cwd = $cwd; + + if ($name) { + $this->configuration[$name] = $name; + } + } + + public function addService(string $service, ?string $image, array $config = []): void + { + if (!isset($this->configuration['services'])) { + $this->configuration['services'] = []; + } + $this->configuration['services'][$service] = array_merge( + isset($image) ? compact('image') : [], + $config + ); + } + + public function extendService(string $service, array $config): void + { + if (!isset($this->configuration['services'][$service])) { + throw new \Exception('Service "' . $service . '" does not exist'); + } + + + } + + public function addServiceVolume(string $service, string $source, string $target, string $modifier = ''): void + { + if (!isset($this->configuration['services'][$service])) { + throw new \Exception('Service "' . $service . '" does not exist'); + } + + if (!isset($this->configuration['services'][$service]['volumes'])) { + $this->configuration['services'][$service]['volumes'] = []; + } + + $this->configuration['services'][$service]['volumes'][] = implode(':', array_filter([ + $source, + $target, + $modifier, + ])); + } + + public function addNetwork(string $network, array $config): void + { + if (!isset($this->configuration['networks'])) { + $this->configuration['networks'] = []; + } + $this->configuration['networks'][$network] = $config; + } + + + public function dump(): string + { + return Yaml::dump($this->configuration, 128); + } +} \ No newline at end of file diff --git a/lib/Creators/NginxConfiguration.php b/lib/Creators/NginxConfiguration.php new file mode 100644 index 0000000000000000000000000000000000000000..fba16eec5b1c1c4b1c1a6916082de2f357a44d53 --- /dev/null +++ b/lib/Creators/NginxConfiguration.php @@ -0,0 +1,109 @@ +<?php +namespace Studip\Dockerized\Creators; + +use RomanPitak\Nginx\Config\Scope; + +final class NginxConfiguration +{ + public const EVENT_LOCATION_ADD_BEFORE = 'location:add:before'; + public const EVENT_SERVER_ADD_BEFORE = 'server:add:before'; + public const EVENT_UPSTREAM_ADD_BEFORE = 'upstream:add:before'; + + private array $locations = []; + private array $servers = []; + private array $upstreams = []; + + private array $event_handlers = []; + + public function __construct() + { + } + + public function on(string $event, \Closure $handler): void + { + if (!isset($this->event_handlers[$event])) { + $this->event_handlers[$event] = []; + } + $this->event_handlers[$event][] = $handler; + } + + private function trigger(string $event, &...$data): void + { + if (isset($this->event_handlers[$event])) { + foreach ($this->event_handlers[$event] as $handler) { + $handler(...$data); + } + } + } + + public function addLocation(string $location, array $config, string $modifier = '^~') + { + $location = '/' . ltrim($location, '/'); + + $this->trigger(self::EVENT_LOCATION_ADD_BEFORE, $location, $config, $modifier); + $this->locations[] = compact('location', 'modifier', 'config'); + } + + public function addServer(array $config) + { + $this->trigger(self::EVENT_SERVER_ADD_BEFORE, $config); + $this->servers[] = compact('config'); + } + + public function addUpstream(string $upstream, array $config) + { + $this->trigger(self::EVENT_UPSTREAM_ADD_BEFORE, $upstream, $config); + $this->upstreams[] = compact('upstream', 'config'); + } + + public function dump(): string + { + $tempName = tempnam(sys_get_temp_dir(), 'nginx'); + file_put_contents($tempName, implode("\n", [ + ...array_map( + fn($upstream) => $this->write( + "upstream {$upstream['upstream']}", + $upstream['config'] + ), + $this->upstreams + ), + ...array_map( + fn($server) => $this->write( + 'server', + array_merge( + $server['config'], + array_map( + fn($location) => $this->write( + "location {$location['modifier']} {$location['location']}", + $location['config'] + ), + $this->locations + ) + ) + ), + $this->servers + ), + ])); + $result = (string) Scope::fromFile($tempName); + unlink($tempName); + + return $result; + } + + public function __toString(): string + { + return $this->dump(); + } + + public function write(string $index, array $content): string + { + return sprintf( + '%s { %s }', + $index, + implode("\n", array_map( + fn (string $what): string => rtrim(trim($what), ';') . ';', + $content + )) + ); + } +} \ No newline at end of file diff --git a/lib/DockerComposeCommand.php b/lib/DockerComposeCommand.php new file mode 100644 index 0000000000000000000000000000000000000000..adc8636e873d7b6e11fc98063153130a96f534b9 --- /dev/null +++ b/lib/DockerComposeCommand.php @@ -0,0 +1,34 @@ +<?php +namespace Studip\Dockerized; + +use Studip\Dockerized\Traits\GetCompiledDockerComposeYMLPath; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Process\Process; + +abstract class DockerComposeCommand extends Command +{ + use GetCompiledDockerComposeYMLPath; + + abstract protected function getDockerComposeCommand(): array; + + protected function execute(InputInterface $input, OutputInterface $output) + { + $process = new Process([ + 'docker', 'compose', + '-f', self::GetCompiledDockerComposeYMLPath(), + ...$this->getDockerComposeCommand(), + ]); + $process->setTty(true); + $process->mustRun(function ($type, $buffer) use ($output) { + if (Process::OUT === $type) { + $output->write($buffer); + } else { + $output->write('<error>' . $buffer . '</error>'); + } + }); + + return $process->isSuccessful() ? Command::SUCCESS : Command::FAILURE; + } +} \ No newline at end of file diff --git a/lib/SupportedPHPVersions.php b/lib/SupportedPHPVersions.php new file mode 100644 index 0000000000000000000000000000000000000000..4db0926c3d75f3f239108a70776fd2f3d97ae096 --- /dev/null +++ b/lib/SupportedPHPVersions.php @@ -0,0 +1,35 @@ +<?php +namespace Studip\Dockerized; + +final class SupportedPHPVersions +{ + private const CONFIGURATION = [ + '7.2' => 8072, + '7.3' => 8073, + '7.4' => 8074, + '8.0' => 8080, + '8.1' => 8081, + '8.2' => 8082, + '8.3' => 8083, + ]; + + private const DEFAULT = ['7.4', '8.1', '8.3']; + + public static function getVersions(): array + { + return array_keys(self::CONFIGURATION); + } + + public static function getDefaultVersions(): array + { + return self::DEFAULT; + } + + public static function mapPort(string $version): int + { + if (!isset(self::CONFIGURATION[$version])) { + throw new \Exception('Unsupported php version'); + } + return self::CONFIGURATION[$version]; + } +} \ No newline at end of file diff --git a/lib/Traits/GetCompiledDockerComposeYMLPath.php b/lib/Traits/GetCompiledDockerComposeYMLPath.php new file mode 100644 index 0000000000000000000000000000000000000000..0c04b9229f26dbac5b2d1aeed0113feb6e7633ec --- /dev/null +++ b/lib/Traits/GetCompiledDockerComposeYMLPath.php @@ -0,0 +1,17 @@ +<?php +namespace Studip\Dockerized\Traits; + +trait GetCompiledDockerComposeYMLPath +{ + protected static function GetCompiledDockerComposeYMLPath(): string + { + $filename = realpath(__DIR__ . '/../../compiled/docker-compose.yml'); + if (!file_exists($filename)) { + throw new \Exception('docker-compose.yml was not yet compiled'); + } + if (!is_readable($filename)) { + throw new \Exception('docker-compose.yml is not readable'); + } + return $filename; + } +} \ No newline at end of file diff --git a/studip-docker b/studip-docker new file mode 100755 index 0000000000000000000000000000000000000000..a1bb30e561cc38b53971820cd00f978a56ab77ca --- /dev/null +++ b/studip-docker @@ -0,0 +1,32 @@ +#!/usr/bin/env php +<?php +require __DIR__ . '/vendor/autoload.php'; + +use Studip\Dockerized\Commands; +use Symfony\Component\Console\Application; + +const CONFIG_FILE = __DIR__ . '/config.json'; + +\Studip\Dockerized\Config::load(CONFIG_FILE); + +$application = new Application('Stud.IP Dockerized', '1.0'); + +$application->add(new Commands\Init()); + +if (file_exists(CONFIG_FILE)) { + $application->add(new Commands\Compile()); + + $application->add(new Commands\Sites\Add()); + $application->add(new Commands\Sites\Remove()); + $application->add(new Commands\Sites\Show()); + + $application->add(new Commands\PHP\Disable()); + $application->add(new Commands\PHP\Enable()); + $application->add(new Commands\PHP\Port()); + + $application->add(new Commands\Docker\Build()); + $application->add(new Commands\Docker\Start()); + $application->add(new Commands\Docker\Stop()); +} + +$application->run(); diff --git a/web/index.php b/web/index.php new file mode 100644 index 0000000000000000000000000000000000000000..8169b242c432de2e8dbe88c2163169d37c058399 --- /dev/null +++ b/web/index.php @@ -0,0 +1,48 @@ +<?php +$config = json_decode(file_get_contents(__DIR__ . '/../studip-dockerized-config.json'), true); +?> +<!doctype html> +<html> +<head> + +</head> +<body> +<style> +html { + font-size: 4em; +} +table { + width: 100%; +} +th { + text-align: left; +} +</style> +<?php if (empty($config['sites'])): ?> + No sites defined +<?php else: ?> + <table> + <thead> + <tr> + <th>Name</th> + <th colspan="<?= count($config['php']) ?>"></th> + </tr> + </thead> + <tbody> + <?php foreach ($config['sites'] ?? [] as $path => $definition): ?> + <tr> + <td><?= htmlentities($path) ?></td> + <?php foreach ($config['php'] as $version => $port): ?> + <td> + <a href="http://localhost:<?= $port ?>/<?= htmlentities($path) ?>"> + <?= htmlentities($version) ?> + </a> + </td> + <?php endforeach; ?> + </tr> + <?php endforeach; ?> + </tbody> + </table> +<?php endif; ?> +</body> +</html>