Initial commit
This commit is contained in:
commit
592f5579ab
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/vendor/
|
||||||
|
/*.phar
|
49
README.md
Normal file
49
README.md
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# ServerCtl - Services for Developers
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Say you need a Mongodb instance for something. Try this:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ serverctl start mongo
|
||||||
|
Started mongo<default>
|
||||||
|
Mongo client: 27017
|
||||||
|
$
|
||||||
|
```
|
||||||
|
|
||||||
|
What just happened? Well, the `start` command pulled and started a new container
|
||||||
|
from the latest docker image using sensible defaults from the service registry.
|
||||||
|
|
||||||
|
Let's start another one:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ serverctl start mongo --instance other --portoffset 1
|
||||||
|
Started mongo<other>
|
||||||
|
Mongo client: 27018
|
||||||
|
$
|
||||||
|
```
|
||||||
|
|
||||||
|
Check it out!
|
||||||
|
|
||||||
|
```
|
||||||
|
$ serverctl status
|
||||||
|
Service Instance Ports
|
||||||
|
mongo default 27017
|
||||||
|
other 27018
|
||||||
|
$ serverctl stop mongo --all
|
||||||
|
Stopped mongo<default>
|
||||||
|
Stopped mongo<other>
|
||||||
|
$
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
### Where is the data stored?
|
||||||
|
|
||||||
|
* Data goes in `$HOME/.var/serverctl`
|
||||||
|
|
9
bin/serverctl
Executable file
9
bin/serverctl
Executable file
@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use NoccyLabs\Serverctl\ConsoleApplication;
|
||||||
|
|
||||||
|
require_once __DIR__."/../vendor/autoload.php";
|
||||||
|
|
||||||
|
$app = new ConsoleApplication();
|
||||||
|
$app->run();
|
29
composer.json
Normal file
29
composer.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "noccylabs/serverctl",
|
||||||
|
"description": "Start and stop services for development",
|
||||||
|
"type": "application",
|
||||||
|
"license": "GPL-3.0-or-later",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"NoccyLabs\\Serverctl\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Christopher Vagnetoft",
|
||||||
|
"email": "cvagnetoft@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"symfony/console": "^6.1",
|
||||||
|
"noccylabs/spinner": "^0.1.0"
|
||||||
|
},
|
||||||
|
"bin": [
|
||||||
|
"bin/serverctl"
|
||||||
|
],
|
||||||
|
"extra": {
|
||||||
|
"phar": {
|
||||||
|
"output": "serverctl.phar"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
761
composer.lock
generated
Normal file
761
composer.lock
generated
Normal file
@ -0,0 +1,761 @@
|
|||||||
|
{
|
||||||
|
"_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": "21d444d8fc7b7dc1b673054b1a48432a",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "noccylabs/spinner",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://dev.noccylabs.info/api/packages/noccy/composer/files/noccylabs%2Fspinner/0.1.0/noccylabs-spinner.0.1.0.zip",
|
||||||
|
"shasum": "5be298051ee740bcb7fa6aac0fa57ae3bbf0771e"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"NoccyLabs\\Spinner\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"license": [
|
||||||
|
"GPL-3.0-or-later"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Christopher Vagnetoft",
|
||||||
|
"email": "cvagnetoft@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Indicate activity in console applications",
|
||||||
|
"time": "2022-09-22T00:11:39+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "psr/container",
|
||||||
|
"version": "2.0.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/php-fig/container.git",
|
||||||
|
"reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
|
||||||
|
"reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.4.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "2.0.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Psr\\Container\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "PHP-FIG",
|
||||||
|
"homepage": "https://www.php-fig.org/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Common Container Interface (PHP FIG PSR-11)",
|
||||||
|
"homepage": "https://github.com/php-fig/container",
|
||||||
|
"keywords": [
|
||||||
|
"PSR-11",
|
||||||
|
"container",
|
||||||
|
"container-interface",
|
||||||
|
"container-interop",
|
||||||
|
"psr"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/php-fig/container/issues",
|
||||||
|
"source": "https://github.com/php-fig/container/tree/2.0.2"
|
||||||
|
},
|
||||||
|
"time": "2021-11-05T16:47:00+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/console",
|
||||||
|
"version": "v6.1.4",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/console.git",
|
||||||
|
"reference": "7fccea8728aa2d431a6725b02b3ce759049fc84d"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/console/zipball/7fccea8728aa2d431a6725b02b3ce759049fc84d",
|
||||||
|
"reference": "7fccea8728aa2d431a6725b02b3ce759049fc84d",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.1",
|
||||||
|
"symfony/deprecation-contracts": "^2.1|^3",
|
||||||
|
"symfony/polyfill-mbstring": "~1.0",
|
||||||
|
"symfony/service-contracts": "^1.1|^2|^3",
|
||||||
|
"symfony/string": "^5.4|^6.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",
|
||||||
|
"symfony/dependency-injection": "^5.4|^6.0",
|
||||||
|
"symfony/event-dispatcher": "^5.4|^6.0",
|
||||||
|
"symfony/lock": "^5.4|^6.0",
|
||||||
|
"symfony/process": "^5.4|^6.0",
|
||||||
|
"symfony/var-dumper": "^5.4|^6.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",
|
||||||
|
"keywords": [
|
||||||
|
"cli",
|
||||||
|
"command line",
|
||||||
|
"console",
|
||||||
|
"terminal"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"source": "https://github.com/symfony/console/tree/v6.1.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": "2022-08-26T10:32:31+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/deprecation-contracts",
|
||||||
|
"version": "v3.1.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/deprecation-contracts.git",
|
||||||
|
"reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918",
|
||||||
|
"reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.1"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-main": "3.1-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.1.1"
|
||||||
|
},
|
||||||
|
"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-02-25T11:15:52+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/polyfill-ctype",
|
||||||
|
"version": "v1.26.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||||
|
"reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4",
|
||||||
|
"reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.1"
|
||||||
|
},
|
||||||
|
"provide": {
|
||||||
|
"ext-ctype": "*"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-ctype": "For best performance"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-main": "1.26-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.26.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": "2022-05-24T11:49:31+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/polyfill-intl-grapheme",
|
||||||
|
"version": "v1.26.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
|
||||||
|
"reference": "433d05519ce6990bf3530fba6957499d327395c2"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/433d05519ce6990bf3530fba6957499d327395c2",
|
||||||
|
"reference": "433d05519ce6990bf3530fba6957499d327395c2",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.1"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-intl": "For best performance"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-main": "1.26-dev"
|
||||||
|
},
|
||||||
|
"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.26.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": "2022-05-24T11:49:31+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/polyfill-intl-normalizer",
|
||||||
|
"version": "v1.26.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
|
||||||
|
"reference": "219aa369ceff116e673852dce47c3a41794c14bd"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd",
|
||||||
|
"reference": "219aa369ceff116e673852dce47c3a41794c14bd",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.1"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-intl": "For best performance"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-main": "1.26-dev"
|
||||||
|
},
|
||||||
|
"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.26.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": "2022-05-24T11:49:31+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/polyfill-mbstring",
|
||||||
|
"version": "v1.26.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||||
|
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
|
||||||
|
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=7.1"
|
||||||
|
},
|
||||||
|
"provide": {
|
||||||
|
"ext-mbstring": "*"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-mbstring": "For best performance"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-main": "1.26-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.26.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": "2022-05-24T11:49:31+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/service-contracts",
|
||||||
|
"version": "v3.1.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/service-contracts.git",
|
||||||
|
"reference": "925e713fe8fcacf6bc05e936edd8dd5441a21239"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/925e713fe8fcacf6bc05e936edd8dd5441a21239",
|
||||||
|
"reference": "925e713fe8fcacf6bc05e936edd8dd5441a21239",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.1",
|
||||||
|
"psr/container": "^2.0"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"ext-psr": "<1.1|>=2"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"symfony/service-implementation": ""
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-main": "3.1-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.1.1"
|
||||||
|
},
|
||||||
|
"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:18:58+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/string",
|
||||||
|
"version": "v6.1.4",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/string.git",
|
||||||
|
"reference": "290972cad7b364e3befaa74ba0ec729800fb161c"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/string/zipball/290972cad7b364e3befaa74ba0ec729800fb161c",
|
||||||
|
"reference": "290972cad7b364e3befaa74ba0ec729800fb161c",
|
||||||
|
"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.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"symfony/error-handler": "^5.4|^6.0",
|
||||||
|
"symfony/http-client": "^5.4|^6.0",
|
||||||
|
"symfony/translation-contracts": "^2.0|^3.0",
|
||||||
|
"symfony/var-exporter": "^5.4|^6.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.1.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": "2022-08-12T18:05:43+00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packages-dev": [],
|
||||||
|
"aliases": [],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"stability-flags": [],
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false,
|
||||||
|
"platform": [],
|
||||||
|
"platform-dev": [],
|
||||||
|
"plugin-api-version": "2.3.0"
|
||||||
|
}
|
30
registry/mariadb.json
Normal file
30
registry/mariadb.json
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"$type": "service",
|
||||||
|
"name": "mariadb",
|
||||||
|
"description": "MariaDB (MySQL fork)",
|
||||||
|
"author": null,
|
||||||
|
"image": "mariadb",
|
||||||
|
"ports": [
|
||||||
|
{ "port": 3306, "info": "MySQL client" }
|
||||||
|
],
|
||||||
|
"environment": {
|
||||||
|
"MARIADB_ROOT_PASSWORD": "toor"
|
||||||
|
},
|
||||||
|
"persistence": [
|
||||||
|
{ "path": "/data", "hint": "data" }
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"mongosh": {
|
||||||
|
"info": "Open the mongo shell",
|
||||||
|
"execute": "mongosh"
|
||||||
|
},
|
||||||
|
"export": {
|
||||||
|
"info": "Export the database",
|
||||||
|
"execute": "mysqldump "
|
||||||
|
},
|
||||||
|
"import": {
|
||||||
|
"info": "Import the database",
|
||||||
|
"execute": "mongoimport"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
registry/mongo.json
Normal file
31
registry/mongo.json
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"$type": "service",
|
||||||
|
"name": "mongo",
|
||||||
|
"description": "MongoDB",
|
||||||
|
"author": null,
|
||||||
|
"image": "mongo",
|
||||||
|
"ports": [
|
||||||
|
{ "port": 27017, "info": "Mongo client" }
|
||||||
|
],
|
||||||
|
"persistence": [
|
||||||
|
{ "path": "/data/db", "hint": "data" }
|
||||||
|
],
|
||||||
|
"environment": {
|
||||||
|
"MONGO_INITDB_ROOT_PASSWORD": "toor",
|
||||||
|
"MONGO_INITDB_ROOT_USERNAME": "root"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"shell": {
|
||||||
|
"info": "Open the mongo shell",
|
||||||
|
"execute": "mongosh"
|
||||||
|
},
|
||||||
|
"export": {
|
||||||
|
"info": "Export the database",
|
||||||
|
"execute": "mongodump"
|
||||||
|
},
|
||||||
|
"import": {
|
||||||
|
"info": "Import the database",
|
||||||
|
"execute": "mongoimport"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
registry/mysql.json
Normal file
30
registry/mysql.json
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"$type": "service",
|
||||||
|
"name": "mysql",
|
||||||
|
"description": "MySQL",
|
||||||
|
"author": null,
|
||||||
|
"image": "mysql",
|
||||||
|
"ports": [
|
||||||
|
{ "port": 3306, "info": "MySQL client" }
|
||||||
|
],
|
||||||
|
"environment": {
|
||||||
|
"MARIADB_ROOT_PASSWORD": "toor"
|
||||||
|
},
|
||||||
|
"persistence": [
|
||||||
|
{ "path": "/data", "hint": "data" }
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"shell": {
|
||||||
|
"info": "Open the mongo shell",
|
||||||
|
"execute": "mysql -uroot -p%{MARIADB_ROOT_PASSWORD}"
|
||||||
|
},
|
||||||
|
"export": {
|
||||||
|
"info": "Export the database",
|
||||||
|
"execute": "mysqldump -uroot -p%{MARIADB_ROOT_PASSWORD}"
|
||||||
|
},
|
||||||
|
"import": {
|
||||||
|
"info": "Import the database",
|
||||||
|
"execute": "mysql -uroot -p%{MARIADB_ROOT_PASSWORD}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
registry/postgres.json
Normal file
30
registry/postgres.json
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"$type": "service",
|
||||||
|
"name": "postgres",
|
||||||
|
"description": "Postgres SQL server",
|
||||||
|
"author": null,
|
||||||
|
"image": "postgres",
|
||||||
|
"ports": [
|
||||||
|
{ "port": 5432, "info": "Postgres client" }
|
||||||
|
],
|
||||||
|
"environment": {
|
||||||
|
"POSTGRES_PASSWORD": "password"
|
||||||
|
},
|
||||||
|
"persistence": [
|
||||||
|
{ "path": "/data", "hint": "data" }
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"mongosh": {
|
||||||
|
"info": "Open a pg shell",
|
||||||
|
"execute": "psql --user postgres"
|
||||||
|
},
|
||||||
|
"export": {
|
||||||
|
"info": "Export the database",
|
||||||
|
"execute": "mysqldump "
|
||||||
|
},
|
||||||
|
"import": {
|
||||||
|
"info": "Import the database",
|
||||||
|
"execute": "mongoimport"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
src/Commands/Command.php
Normal file
26
src/Commands/Command.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NoccyLabs\Serverctl\Commands;
|
||||||
|
|
||||||
|
use NoccyLabs\Serverctl\ConsoleApplication;
|
||||||
|
use NoccyLabs\Serverctl\Container\ContainerManager;
|
||||||
|
use NoccyLabs\Serverctl\Registry\ServiceRegistry;
|
||||||
|
use Symfony\Component\Console\Command\Command as CommandCommand;
|
||||||
|
|
||||||
|
abstract class Command extends CommandCommand
|
||||||
|
{
|
||||||
|
public function getApplication(): ?ConsoleApplication
|
||||||
|
{
|
||||||
|
return parent::getApplication();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getServiceRegistry(): ServiceRegistry
|
||||||
|
{
|
||||||
|
return $this->getApplication()->getServiceRegistry();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getContainerManager(): ContainerManager
|
||||||
|
{
|
||||||
|
return $this->getApplication()->getContainerManager();
|
||||||
|
}
|
||||||
|
}
|
51
src/Commands/ExecCommand.php
Normal file
51
src/Commands/ExecCommand.php
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NoccyLabs\Serverctl\Commands;
|
||||||
|
|
||||||
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
#[AsCommand(name:"exec", description:"Execute a shell command or script in the service")]
|
||||||
|
class ExecCommand extends Command
|
||||||
|
{
|
||||||
|
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this->addOption("instance", "I", InputOption::VALUE_REQUIRED, "Specify the instance name", "default");
|
||||||
|
$this->addOption("script", "s", InputOption::VALUE_NONE, "The command is a script (use 'list' for list)");
|
||||||
|
$this->addArgument("service", InputArgument::REQUIRED, "The service name");
|
||||||
|
$this->addArgument("execute", InputArgument::REQUIRED, "The command or script to execute");
|
||||||
|
$this->addArgument("arguments", InputArgument::IS_ARRAY, "Arguments");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
|
{
|
||||||
|
$serviceRegistry = $this->getServiceRegistry();
|
||||||
|
$containerManager = $this->getContainerManager();
|
||||||
|
$instanceName = $input->getOption("instance");
|
||||||
|
|
||||||
|
$serviceName = $input->getArgument("service");
|
||||||
|
$command = $input->getArgument("execute");
|
||||||
|
|
||||||
|
$serviceInfo = $serviceRegistry->findServiceByName($serviceName);
|
||||||
|
|
||||||
|
if ($command == "list") {
|
||||||
|
$scripts = (array)($serviceInfo['scripts']??[]);
|
||||||
|
$output->writeln("Available scripts:");
|
||||||
|
foreach ($scripts as $script=>$meta) {
|
||||||
|
$output->writeln(sprintf(" <comment>%s</> - <info>%s</>", $script, $meta['info']??"?"));
|
||||||
|
}
|
||||||
|
return self::SUCCESS;
|
||||||
|
} else {
|
||||||
|
$cmdl = [ $input->getArgument("execute") ];
|
||||||
|
array_push($cmdl, ...$input->getArgument("arguments"));
|
||||||
|
$containerManager->execute($serviceInfo, $instanceName, $cmdl);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
33
src/Commands/FindCommand.php
Normal file
33
src/Commands/FindCommand.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NoccyLabs\Serverctl\Commands;
|
||||||
|
|
||||||
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
#[AsCommand(name:"find", description:"Find defined services")]
|
||||||
|
class FindCommand extends Command
|
||||||
|
{
|
||||||
|
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this->addArgument("service", InputArgument::OPTIONAL, "Search query");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
|
{
|
||||||
|
$serviceRegistry = $this->getServiceRegistry();
|
||||||
|
|
||||||
|
$services = $serviceRegistry->findAllServices();
|
||||||
|
|
||||||
|
$output->writeln("Available services:");
|
||||||
|
foreach ($services as $service) {
|
||||||
|
$output->writeln(sprintf(" <comment>%s</> - <info>%s</>", $service['name'], $service['description']??"?"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
45
src/Commands/StartCommand.php
Normal file
45
src/Commands/StartCommand.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NoccyLabs\Serverctl\Commands;
|
||||||
|
|
||||||
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
#[AsCommand(name:"start", description:"Start a service")]
|
||||||
|
class StartCommand extends Command
|
||||||
|
{
|
||||||
|
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this->addOption("instance", "I", InputOption::VALUE_REQUIRED, "Specify the instance name", "default");
|
||||||
|
$this->addOption("portoffset", "p", InputOption::VALUE_REQUIRED, "Offset port numbers by value", 0);
|
||||||
|
$this->addArgument("service", InputArgument::REQUIRED, "The service name");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
|
{
|
||||||
|
$serviceRegistry = $this->getServiceRegistry();
|
||||||
|
$containerManager = $this->getContainerManager();
|
||||||
|
|
||||||
|
|
||||||
|
$serviceName = $input->getArgument("service");
|
||||||
|
|
||||||
|
$serviceInfo = $serviceRegistry->findServiceByName($serviceName);
|
||||||
|
if (!$serviceInfo) {
|
||||||
|
$output->writeln("<error>No such service in registry</>");
|
||||||
|
return self::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$options = [
|
||||||
|
'name' => $input->getOption("instance"),
|
||||||
|
'portoffset' => $input->getOption("portoffset")
|
||||||
|
];
|
||||||
|
|
||||||
|
$containerManager->startService($serviceInfo, $options);
|
||||||
|
|
||||||
|
return self::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
34
src/Commands/StatusCommand.php
Normal file
34
src/Commands/StatusCommand.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NoccyLabs\Serverctl\Commands;
|
||||||
|
|
||||||
|
use NoccyLabs\Spinner\Spinner;
|
||||||
|
use NoccyLabs\Spinner\Style\BrailleDotsStyle;
|
||||||
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
#[AsCommand(name:"status", description:"Show the running services")]
|
||||||
|
class StatusCommand extends Command
|
||||||
|
{
|
||||||
|
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
|
{
|
||||||
|
|
||||||
|
$spinner = new Spinner(style: BrailleDotsStyle::class, fps: 15);
|
||||||
|
|
||||||
|
$output->write(" Getting status\r");
|
||||||
|
for ($n = 0; $n < 250; $n++) {
|
||||||
|
if ($output->isDecorated()) $output->write("({$spinner})\r");
|
||||||
|
usleep(10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return self::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
39
src/Commands/StopCommand.php
Normal file
39
src/Commands/StopCommand.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NoccyLabs\Serverctl\Commands;
|
||||||
|
|
||||||
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
#[AsCommand(name:"stop", description:"Stop a running service")]
|
||||||
|
class StopCommand extends Command
|
||||||
|
{
|
||||||
|
|
||||||
|
protected function configure()
|
||||||
|
{
|
||||||
|
$this->addOption("instance", "I", InputOption::VALUE_REQUIRED, "Specify the instance name", "default");
|
||||||
|
$this->addArgument("service", InputArgument::REQUIRED, "The service name");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output)
|
||||||
|
{
|
||||||
|
$serviceRegistry = $this->getServiceRegistry();
|
||||||
|
$containerManager = $this->getContainerManager();
|
||||||
|
|
||||||
|
|
||||||
|
$serviceName = $input->getArgument("service");
|
||||||
|
|
||||||
|
$serviceInfo = $serviceRegistry->findServiceByName($serviceName);
|
||||||
|
if (!$serviceInfo) {
|
||||||
|
$output->writeln("<error>No such service in registry</>");
|
||||||
|
return self::FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$containerManager->stopService($serviceInfo, $input->getOption("instance"));
|
||||||
|
|
||||||
|
return self::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
48
src/ConsoleApplication.php
Normal file
48
src/ConsoleApplication.php
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NoccyLabs\Serverctl;
|
||||||
|
|
||||||
|
use NoccyLabs\Serverctl\Container\ContainerManager;
|
||||||
|
use NoccyLabs\Serverctl\Registry\ServiceRegistry;
|
||||||
|
use Symfony\Component\Console\Application;
|
||||||
|
|
||||||
|
class ConsoleApplication extends Application
|
||||||
|
{
|
||||||
|
|
||||||
|
private ServiceRegistry $serviceRegistry;
|
||||||
|
|
||||||
|
private ContainerManager $containerManager;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct("Development server utility", "0.1.0");
|
||||||
|
|
||||||
|
$this->serviceRegistry = new ServiceRegistry(
|
||||||
|
paths: [
|
||||||
|
__DIR__."/../registry",
|
||||||
|
dirname(realpath($GLOBALS['argv'][0]))."/registry",
|
||||||
|
"/usr/share/serverctl/registry",
|
||||||
|
getenv("HOME")."/.share/serverctl/registry"
|
||||||
|
]
|
||||||
|
);
|
||||||
|
$this->containerManager = new ContainerManager(
|
||||||
|
dataPath: null
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->add(new Commands\StartCommand());
|
||||||
|
$this->add(new Commands\StopCommand());
|
||||||
|
$this->add(new Commands\ExecCommand());
|
||||||
|
$this->add(new Commands\FindCommand());
|
||||||
|
$this->add(new Commands\StatusCommand());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getServiceRegistry(): ServiceRegistry
|
||||||
|
{
|
||||||
|
return $this->serviceRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContainerManager(): ContainerManager
|
||||||
|
{
|
||||||
|
return $this->containerManager;
|
||||||
|
}
|
||||||
|
}
|
132
src/Container/ContainerManager.php
Normal file
132
src/Container/ContainerManager.php
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NoccyLabs\Serverctl\Container;
|
||||||
|
|
||||||
|
class ContainerManager
|
||||||
|
{
|
||||||
|
|
||||||
|
private string $dataPath;
|
||||||
|
|
||||||
|
public function __construct(?string $dataPath=null)
|
||||||
|
{
|
||||||
|
$this->dataPath = $dataPath ?? (getenv("HOME")."/.var/serverctl");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a service
|
||||||
|
*
|
||||||
|
* Instance options:
|
||||||
|
* name: Instance name (default)
|
||||||
|
* portoffset: Port number offset (0)
|
||||||
|
*
|
||||||
|
* @param array $service The service definition from the registry
|
||||||
|
* @param array $options Instance options
|
||||||
|
*/
|
||||||
|
public function startService(array $service, array $options)
|
||||||
|
{
|
||||||
|
$args = [];
|
||||||
|
|
||||||
|
$serviceName = $service['name'];
|
||||||
|
$instanceName = $options['name']??'default';
|
||||||
|
|
||||||
|
$containerName = "sm_".$serviceName."_".$instanceName;
|
||||||
|
|
||||||
|
$args[] = 'run';
|
||||||
|
$args[] = '--rm'; // remove container after run
|
||||||
|
$args[] = '-d';
|
||||||
|
$args[] = '--name';
|
||||||
|
$args[] = $containerName;
|
||||||
|
|
||||||
|
// Map the ports
|
||||||
|
$ports = (array)($service['ports']??[]);
|
||||||
|
foreach ($ports as $port) {
|
||||||
|
$args[] = '-p';
|
||||||
|
$args[] = $port['port'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the paths to persist
|
||||||
|
$volumes = (array)($service['persistence']??[]);
|
||||||
|
$volumePath = $this->getServiceDataPath($service)."/".$instanceName;
|
||||||
|
foreach ($volumes as $volume) {
|
||||||
|
// volume { path, hint }
|
||||||
|
$path = $volume['path'];
|
||||||
|
$hint = $volume['hint'] ?? crc32($path);
|
||||||
|
$args[] = '-v'; // add volume
|
||||||
|
$args[] = $volumePath."/".$hint.":".$path;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get environment
|
||||||
|
$envs = (array)($service['environment']??[]);
|
||||||
|
foreach ($envs as $env=>$value) {
|
||||||
|
$args[] = '-e';
|
||||||
|
// TODO: use environment if set (override)
|
||||||
|
$args[] = sprintf("%s=%s", $env, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
$args[] = $service['image'];
|
||||||
|
|
||||||
|
$cmdl = 'docker '.join(' ',array_map('escapeshellarg', $args));
|
||||||
|
|
||||||
|
// TODO: Write command line, env and meta to state file
|
||||||
|
|
||||||
|
echo "$ {$cmdl}\n";
|
||||||
|
passthru($cmdl);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop a service
|
||||||
|
*/
|
||||||
|
public function stopService(array $service, string $instanceName)
|
||||||
|
{
|
||||||
|
$args = [];
|
||||||
|
|
||||||
|
$serviceName = $service['name'];
|
||||||
|
$instanceName = $options['name']??'default';
|
||||||
|
$containerName = "sm_".$serviceName."_".$instanceName;
|
||||||
|
|
||||||
|
$args[] = 'stop';
|
||||||
|
$args[] = $containerName;
|
||||||
|
|
||||||
|
$cmdl = 'docker '.join(' ',array_map('escapeshellarg', $args));
|
||||||
|
|
||||||
|
echo "$ {$cmdl}\n";
|
||||||
|
passthru($cmdl);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute(array $service, string $instanceName, array $command)
|
||||||
|
{
|
||||||
|
$args = [];
|
||||||
|
|
||||||
|
$serviceName = $service['name'];
|
||||||
|
$instanceName = $options['name']??'default';
|
||||||
|
$containerName = "sm_".$serviceName."_".$instanceName;
|
||||||
|
|
||||||
|
$args[] = 'exec';
|
||||||
|
$args[] = '-it';
|
||||||
|
$args[] = $containerName;
|
||||||
|
|
||||||
|
array_push($args, ...$command);
|
||||||
|
|
||||||
|
$cmdl = 'docker '.join(' ',array_map('escapeshellarg', $args));
|
||||||
|
|
||||||
|
echo "$ {$cmdl}\n";
|
||||||
|
passthru($cmdl);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get running services
|
||||||
|
*/
|
||||||
|
public function getRunningServices(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getServiceDataPath(array $service)
|
||||||
|
{
|
||||||
|
return $this->dataPath."/".$service['name'];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
57
src/Registry/ServiceRegistry.php
Normal file
57
src/Registry/ServiceRegistry.php
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NoccyLabs\Serverctl\Registry;
|
||||||
|
|
||||||
|
use RecursiveDirectoryIterator;
|
||||||
|
use RecursiveIteratorIterator;
|
||||||
|
use SplFileInfo;
|
||||||
|
|
||||||
|
class ServiceRegistry
|
||||||
|
{
|
||||||
|
|
||||||
|
private $services = [];
|
||||||
|
|
||||||
|
public function __construct(array $paths)
|
||||||
|
{
|
||||||
|
foreach ($paths as $path) {
|
||||||
|
if (is_dir($path)) {
|
||||||
|
$this->readPath($path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function readPath(string $path)
|
||||||
|
{
|
||||||
|
$iter = new RecursiveIteratorIterator(
|
||||||
|
new RecursiveDirectoryIterator(
|
||||||
|
$path
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
/** @var SplFileInfo $iteminfo */
|
||||||
|
foreach ($iter as $itempath=>$iteminfo) {
|
||||||
|
if (!fnmatch("*.json", $itempath)) continue;
|
||||||
|
$json = file_get_contents($itempath);
|
||||||
|
$parsed = json_decode($json, true);
|
||||||
|
$this->services[$itempath] = $parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
usort($this->services, function ($a,$b) {
|
||||||
|
return $a['name'] <=> $b['name'];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findServiceByName(string $name): ?array
|
||||||
|
{
|
||||||
|
foreach ($this->services as $service) {
|
||||||
|
if ($service['name'] === $name) return $service;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAllServices(): array
|
||||||
|
{
|
||||||
|
return $this->services;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user