diff --git a/.env b/.env index 0c8c7f5..90ba3b4 100644 --- a/.env +++ b/.env @@ -3,3 +3,6 @@ PM_APP_URL=http://localhost PM_APP_PORT=8080 PM_BROADCASTER_PORT=6004 PM_DOCKER_SOCK=/var/run/docker.sock + +CI_PROJECT=processmaker +CI_PACKAGE_BRANCH=updates-for-ci \ No newline at end of file diff --git a/.env-cicd b/.env-cicd new file mode 100644 index 0000000..0e8e277 --- /dev/null +++ b/.env-cicd @@ -0,0 +1,23 @@ +PM_VERSION=local +CODEBASE_PATH=/code/pm4 +PACKAGES_PATH=/code/pm4-packages +PM_CI=true +PM_APP_URL=http://localhost +PM_APP_PORT=8080 +PM_BROADCASTER_PORT=6004 +PROCESSMAKER_SCRIPTS_DOCKER=/usr/local/bin/docker +LARAVEL_ECHO_SERVER_AUTH_HOST=http://localhost +SESSION_SECURE_COOKIE=false +CACHE_DRIVER=array +SESSION_DOMAIN=null +GOOGLE_API_TOKEN=test +SLACK_OAUTH_ACCESS_TOKEN=test +DB_HOSTNAME=mysql +DB_PORT=3306 +DB_USERNAME=root +DB_PASSWORD=password +REDIS_HOST=redis +TELESCOPE_ENABLED=false +BUILD_JAVASCRIPT=true + +# COMPOSER_MIRROR_PATH_REPOS=1 diff --git a/.gitignore b/.gitignore index 6a01034..080fa4c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ -bind-mount/composer/cache -bind-mount/npm \ No newline at end of file +pmrepo +cache +.DS_Store diff --git a/Dockerfile.base b/Dockerfile.base index cfe43b7..a573868 100644 --- a/Dockerfile.base +++ b/Dockerfile.base @@ -5,13 +5,13 @@ ENV TZ="America/Los_Angeles" RUN apt update # In ubuntu 20.04, installing php without specifying a version installs 7.4 :) -RUN apt install -y php php-cli php-fpm php-json php-common php-mysql php-zip php-gd php-mbstring php-curl php-xml php-pear php-bcmath php-imagick php-dom php-sqlite3 \ -nginx vim curl unzip wget supervisor cron mysql-client build-essential +RUN apt install -y php php-cli php-fpm php-json php-common php-mysql php-zip php-gd php-mbstring php-curl php-xml php-pear php-bcmath php-imagick php-dom php-sqlite3 php-imap \ +nginx vim curl git unzip wget supervisor cron mysql-client build-essential # # node # -RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - +RUN curl -sL https://deb.nodesource.com/setup_16.x | bash - RUN apt -y install nodejs # @@ -51,7 +51,16 @@ COPY build-files/nginx.conf /etc/nginx/nginx.conf # COPY build-files/services.conf /etc/supervisor/conf.d/services.conf +# +# laravel echo server +# +RUN mkdir -p /code +COPY build-files/laravel-echo-server.json /code + +# populate $_ENV with environment variables +RUN echo "variables_order = \"EGPCS\"" > /etc/php/7.4/cli/conf.d/30-env.ini + +# Use the correct version of mysqldump +COPY --from=mysql:5.7 /usr/bin/mysqldump /usr/bin/mysqldump -RUN mkdir -p /code/pm4 -WORKDIR /code/pm4 EXPOSE 80 443 6001 \ No newline at end of file diff --git a/Dockerfile.cicd b/Dockerfile.cicd new file mode 100644 index 0000000..b4e7ee5 --- /dev/null +++ b/Dockerfile.cicd @@ -0,0 +1,15 @@ +ARG BASE_VERSION +FROM processmaker/pm4-base:${BASE_VERSION} + +RUN npm config set cache /cache/npm --global +ENV COMPOSER_HOME=/cache/composer + +COPY ./pm4-tools /code/pm4-tools +WORKDIR /code/pm4-tools +RUN composer install --no-interaction + +WORKDIR /code +COPY build-files/cicd-build-run.sh . +COPY build-files/cicd-run-app.sh . +COPY build-files/cicd-run-phpunit.sh . +COPY packages-to-install.php . \ No newline at end of file diff --git a/Dockerfile b/Dockerfile.core-app similarity index 89% rename from Dockerfile rename to Dockerfile.core-app index 999dac4..61b1c82 100644 --- a/Dockerfile +++ b/Dockerfile.core-app @@ -13,4 +13,4 @@ RUN npm install --unsafe-perm=true && npm run dev COPY build-files/laravel-echo-server.json . COPY build-files/init.sh . -CMD bash init.sh && supervisord --nodaemon +CMD bash core-build-run.sh && supervisord --nodaemon \ No newline at end of file diff --git a/Dockerfile.php81.base b/Dockerfile.php81.base new file mode 100644 index 0000000..e0ac28e --- /dev/null +++ b/Dockerfile.php81.base @@ -0,0 +1,77 @@ +FROM ubuntu:20.04 +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ="America/Los_Angeles" + +RUN apt update +RUN apt install -y software-properties-common +RUN add-apt-repository ppa:ondrej/php + +RUN apt install -y php8.1 php8.1-cli php8.1-fpm php8.1-common +RUN apt install -y php8.1-mysql php8.1-zip php8.1-gd php8.1-mbstring php8.1-curl php8.1-xml php8.1-bcmath php8.1-imagick php8.1-dom php8.1-sqlite3 php8.1-imap +RUN apt install -y nginx vim curl git unzip wget supervisor cron mysql-client build-essential + +# +# node +# +# RUN curl -sL https://deb.nodesource.com/setup_16.x | bash - +# RUN apt -y install nodejs + +ENV NVM_DIR /root/.nvm +ENV NODE_VERSION 16.18.1 +RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash && \ + . "$NVM_DIR/nvm.sh" && \ + nvm install $NODE_VERSION && \ + nvm alias default $NODE_VERSION && \ + nvm use default +ENV NODE_PATH $NVM_DIR/versions/node/v$NODE_VERSION/lib/node_modules +ENV PATH $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH + +RUN npm install -g npm@8.19 + +# +# install composer +# +RUN wget -O composer-setup.php https://getcomposer.org/installer +RUN php composer-setup.php --install-dir=/usr/local/bin --filename=composer +RUN composer self-update + +# +# cron +# +COPY build-files/laravel-cron /etc/cron.d/laravel-cron +RUN chmod 0644 /etc/cron.d/laravel-cron && crontab /etc/cron.d/laravel-cron + +# +# docker client +# +ENV DOCKERVERSION=20.10.5 +RUN curl -fsSLO https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKERVERSION}.tgz \ + && tar xzvf docker-${DOCKERVERSION}.tgz --strip 1 \ + -C /usr/local/bin docker/docker \ + && rm docker-${DOCKERVERSION}.tgz + +# +# configure php-fpm to run as root +# +RUN sed -i 's/www-data/root/g' /etc/php/8.1/fpm/pool.d/www.conf + +# +# nginx +# +COPY build-files/nginx.conf /etc/nginx/nginx.conf + +# +# supervisord +# +COPY build-files/services.conf /etc/supervisor/conf.d/services.conf + +# +# laravel echo server +# +RUN mkdir -p /code +COPY build-files/laravel-echo-server.json /code + +# populate $_ENV with environment variables +RUN echo "variables_order = \"EGPCS\"" > /etc/php/8.1/cli/conf.d/30-env.ini + +EXPOSE 80 443 6001 \ No newline at end of file diff --git a/README.md b/README.md index ce46120..3ac3a97 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,37 @@ This allows for smaller images and better performance than using dind (docker in See [this post](http://jpetazzo.github.io/2015/09/03/do-not-use-docker-in-docker-for-ci/) for more info. The host socket file is usually at /var/run/docker.sock but can be changed in the .env file +# ProcessMaker CI/CD Build +This repository includes our setup for internal CI/CD builds run by Github Actions. This includes all enterprise packages. +## Running locally +1. Create a github personal access token with access to all enterprise packages and set it as an environment variable called GITHUB_TOKEN + ``` + export GITHUB_TOKEN=ghp_........ + ``` +2. Source set_context_example.sh to set an example GITHUB_CONTEXT variable with the PR body from pr_body_example.txt + ``` + source set_context_example.sh + ``` +3. (Optional) Build the base image (otherwise it will get pulled from dockerhub) + ``` + docker-compose build base + ``` +4. Build the local CI/CD image + ``` + docker-compose build cicd + ``` +5. Run the image to build the app and execute phpunit tests + ``` + docker-compose run cicd + ``` +6. To debug, commit the container to an image and run bash. To find the container ID run `docker ps -a` and look for the name pm4core-docker_builder_run... + ``` + docker commit {container id} debug:latest + docker run --rm -it debug bash + ``` + You can then CD into /code/pm4 and run individual phpunit tests + + ## Todo ### Automated builds pushed to dockerhub diff --git a/build-docker-image.sh b/build-docker-image.sh new file mode 100755 index 0000000..4e102ce --- /dev/null +++ b/build-docker-image.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +docker commit $(./container.sh) processmaker/pm4app:"$IMAGE_TAG" +echo "$DOCKERHUB_TOKEN" | docker login -u processmaker --password-stdin +docker push processmaker/pm4app:"$IMAGE_TAG" diff --git a/build-files/cicd-build-run.sh b/build-files/cicd-build-run.sh new file mode 100644 index 0000000..1d932cc --- /dev/null +++ b/build-files/cicd-build-run.sh @@ -0,0 +1,19 @@ +set -ex + +while ! mysqladmin ping -u $DB_USERNAME -p"$DB_PASSWORD" -h $DB_HOSTNAME -P $DB_PORT --silent; do + echo "Waiting for mysql" + sleep 1 +done + +composer config --global github-oauth.github.com $GITHUB_TOKEN + +/code/pm4-tools/pm build-ci + +mv /code/laravel-echo-server.json /code/pm4/ + +/code/pm4-tools/pm build-javascript-ci + +DB_DATABASE=processmaker \ + /code/pm4-tools/pm install-ci + +cd /code/pm4 diff --git a/build-files/cicd-run-app.sh b/build-files/cicd-run-app.sh new file mode 100644 index 0000000..1bb8bc7 --- /dev/null +++ b/build-files/cicd-run-app.sh @@ -0,0 +1,15 @@ +set -ex + +while ! mysqladmin ping -u $DB_USERNAME -p"$DB_PASSWORD" -h $DB_HOSTNAME -P $DB_PORT --silent; do + echo "Waiting for mysql" + sleep 1 +done + +mysql -u $DB_USERNAME -p"$DB_PASSWORD" -h $DB_HOSTNAME -P $DB_PORT -e "DROP DATABASE IF EXISTS $DB_DATABASE;" +mysql -u $DB_USERNAME -p"$DB_PASSWORD" -h $DB_HOSTNAME -P $DB_PORT -e "CREATE DATABASE $DB_DATABASE;" +mysql -u $DB_USERNAME -p"$DB_PASSWORD" -h $DB_HOSTNAME -P $DB_PORT $DB_DATABASE < database.sql + +cd /code/pm4 +PM_CI=false php artisan docker-executor-php:install + +supervisord --nodaemon \ No newline at end of file diff --git a/build-files/cicd-run-phpunit.sh b/build-files/cicd-run-phpunit.sh new file mode 100644 index 0000000..07ee10e --- /dev/null +++ b/build-files/cicd-run-phpunit.sh @@ -0,0 +1,14 @@ +set -ex + +while ! mysqladmin ping -u $DB_USERNAME -p"$DB_PASSWORD" -h $DB_HOSTNAME -P $DB_PORT --silent; do + echo "Waiting for mysql" + sleep 1 +done + +mysql -u $DB_USERNAME -p"$DB_PASSWORD" -h $DB_HOSTNAME -P $DB_PORT -e "DROP DATABASE IF EXISTS test;" +mysql -u $DB_USERNAME -p"$DB_PASSWORD" -h $DB_HOSTNAME -P $DB_PORT -e "CREATE DATABASE test;" +# mysql -u $DB_USERNAME -p"$DB_PASSWORD" -h $DB_HOSTNAME -P $DB_PORT test < database.sql + +cd /code/pm4 +# vendor/bin/paratest -p $PARALLEL_TEST_PROCESSES +vendor/bin/phpunit --testdox \ No newline at end of file diff --git a/build-files/init.sh b/build-files/core-build-run.sh similarity index 74% rename from build-files/init.sh rename to build-files/core-build-run.sh index 4c6b682..50c000a 100644 --- a/build-files/init.sh +++ b/build-files/core-build-run.sh @@ -1,18 +1,5 @@ set -ex -# # Wait for mysql to bec available -# while ! mysqladmin ping -u pm -ppass -h mysql --silent; do -# echo "Waiting for mysql" -# sleep 1 -# done - -# # Check if database has been initialized -# NUM_TABLES=$(mysql -u pm -p'pass' -h mysql -N -B -e 'show tables;' processmaker | wc -l) - -# if [ $NUM_TABLES -eq 0 ]; then -# mysql -u pm -p'pass' -h mysql processmaker < mysqldump.sql -# fi - if [ ! -f ".env" ]; then while ! mysqladmin ping -u pm -ppass -h mysql --silent; do @@ -52,4 +39,4 @@ if [ ! -f ".env" ]; then echo "PROCESSMAKER_SCRIPTS_DOCKER_MODE=copying" >> .env echo "LARAVEL_ECHO_SERVER_AUTH_HOST=http://localhost" >> .env echo "SESSION_SECURE_COOKIE=false" >> .env -fi +fi \ No newline at end of file diff --git a/build-files/services.conf b/build-files/services.conf index 5445350..370770f 100644 --- a/build-files/services.conf +++ b/build-files/services.conf @@ -15,6 +15,7 @@ stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr stderr_logfile_maxbytes=0 +autorestart=true [program:laravel-echo-server] directory=/code/pm4 diff --git a/container.sh b/container.sh new file mode 100755 index 0000000..074d1e1 --- /dev/null +++ b/container.sh @@ -0,0 +1 @@ +docker ps -aqlf "name=cicd_run" diff --git a/deploy-to-qa.sh b/deploy-to-qa.sh new file mode 100644 index 0000000..47bbc21 --- /dev/null +++ b/deploy-to-qa.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +git clone https://github.com/ProcessMaker/pm4core-docker.git --branch v2 --depth 1 + +sed -i "s#\${CI_PROJECT}#$CI_PROJECT#g" pm4core-docker/docker-compose.yml +sed -i "s#\${CI_PACKAGE_BRANCH}#$CI_PACKAGE_BRANCH#g" pm4core-docker/docker-compose.yml +sed -i "s#\${PM_APP_PORT}#$PM_APP_PORT#g" pm4core-docker/docker-compose.yml +sed -i "s#\${PM_BROADCASTER_PORT}#$PM_BROADCASTER_PORT#g" pm4core-docker/docker-compose.yml +sed -i "s#\${PM_DOCKER_SOCK}#/var/run/docker.sock#g" pm4core-docker/docker-compose.yml +sed -i "s#\${IMAGE_TAG}#$IMAGE_TAG#g" pm4core-docker/docker-compose.yml + +ssh ssh-qapackages-pm4 " sudo rm -rf /home/DeployQA/$CI_PROJECT-$CI_PACKAGE_BRANCH/*" +ssh ssh-qapackages-pm4 " sudo mkdir -p /home/DeployQA/$CI_PROJECT-$CI_PACKAGE_BRANCH" +ssh ssh-qapackages-pm4 " sudo chmod 777 /home/DeployQA/$CI_PROJECT-$CI_PACKAGE_BRANCH" +scp -r -o StrictHostKeyChecking=no -o ProxyCommand='ssh -o StrictHostKeyChecking=no buildbot@buildbot.processmaker.net -A -W %h:%p' pm4core-docker buildbot@qa-pm4packages:/home/DeployQA/$CI_PROJECT-$CI_PACKAGE_BRANCH/ +ssh ssh-qapackages-pm4 " sudo docker login -u=processmaker -p=$DOCKERHUB_TOKEN" +ssh ssh-qapackages-pm4 " sudo docker-compose -p $CI_PROJECT-$CI_PACKAGE_BRANCH -f /home/DeployQA/$CI_PROJECT-$CI_PACKAGE_BRANCH/pm4core-docker/docker-compose.yml up web >/dev/null 2>&1 &" diff --git a/docker-compose.yml b/docker-compose.yml index 8127f6d..f9449be 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,35 +1,101 @@ version: "3.8" services: + cicd: + image: cicd:latest + build: + context: . + dockerfile: Dockerfile.cicd + args: + - BASE_VERSION=${BASE_VERSION:-2.1.7} + command: sh cicd-build-run.sh + environment: + - GITHUB_TOKEN + - CI_PR_BODY + - CI_PROJECT + - CI_PACKAGE_BRANCH + - BASE_VERSION + env_file: + - .env-cicd + links: + - redis + - mysql + depends_on: + - mysql + - redis + volumes: + - ./cache:/cache + + base-old: + image: processmaker/pm4-base:2.0.0 + build: + context: . + dockerfile: Dockerfile.base + + base-php81: + image: processmaker/pm4-base:${BASE_VERSION:-2.1.7} + build: + context: . + dockerfile: Dockerfile.php81.base + + redis: + image: redis + volumes: + - redis:/data + + debug: + image: processmaker/pm4app:${IMAGE_TAG} + command: bash + env_file: + - .env-cicd + links: + - mysql + depends_on: + - mysql + + phpunit: + image: processmaker/pm4app:${IMAGE_TAG} + command: sh cicd-run-phpunit.sh + env_file: + - .env-cicd + links: + - mysql + - redis + depends_on: + - mysql + - redis + web: - image: processmaker/pm4-core:${PM_VERSION} + image: processmaker/pm4app:${IMAGE_TAG} + command: sh cicd-run-app.sh ports: - ${PM_APP_PORT}:80 - ${PM_BROADCASTER_PORT}:6001 + - 9001:9001 environment: - PM_APP_URL - PM_APP_PORT - PM_BROADCASTER_PORT + - DB_DATABASE=processmaker + - PM_CI=false + - CACHE_DRIVER=redis volumes: - ${PM_DOCKER_SOCK}:/var/run/docker.sock - storage:/code/pm4/storage links: - redis - - mysql + - mysql depends_on: - mysql - - redis - redis: - image: redis + - redis + mysql: - image: mysql:5.7 + image: mysql:8 restart: always environment: - MYSQL_ROOT_PASSWORD: password + MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: processmaker - MYSQL_USER: pm - MYSQL_PASSWORD: pass - volumes: - - database:/var/lib/mysql + tmpfs: /var/lib/mysql + volumes: - database: - storage: \ No newline at end of file + redis: + storage: diff --git a/packages-to-install.php b/packages-to-install.php new file mode 100644 index 0000000..842b939 --- /dev/null +++ b/packages-to-install.php @@ -0,0 +1,19 @@ + [ 'tests' => false ], + 'package-comments' => [ 'tests' => false ], + 'package-googleplaces' => [ 'tests' => false ], + 'package-process-documenter' => [ 'tests' => false ], + 'package-process-optimization' => [ 'tests' => false ], + 'package-sentry' => [ 'tests' => false ], + 'package-signature' => [ 'tests' => false ], + 'packages' => [ 'tests' => false ], + + 'package-versions' => [ 'tests' => true ], + 'package-translations' => [ 'tests' => true ], + + // Add new packages here or enable tests only after unit test fixes are merged into develop + 'connector-send-email' => [ 'tests' => false ], + 'package-collections' => [ 'tests' => false ], + 'package-savedsearch' => [ 'tests' => false ], +]; diff --git a/pm4-tools/cli/ProcessMaker/CommandLine.php b/pm4-tools/cli/ProcessMaker/CommandLine.php new file mode 100644 index 0000000..a092229 --- /dev/null +++ b/pm4-tools/cli/ProcessMaker/CommandLine.php @@ -0,0 +1,196 @@ +time = microtime(true); + } + + /** + * Returns the timing (in seconds) since the + * CommandLine class was instantiated + * + * @return string + */ + public function timing(): string + { + $seconds = round(abs($this->time - microtime(true)), 2); + $minutes = round($seconds / 60, 2); + $hours = round($minutes / 60, 2); + + if ($hours >= 1.00) { + return "$hours hours"; + } + + if ($minutes >= 1.00) { + return "$minutes minutes"; + } + + return "$seconds seconds"; + } + + /** + * Simple global function to run commands. + * + * @param string $command + * + * @return void + */ + public function quietly(string $command) + { + $this->runCommand($command.' > /dev/null 2>&1'); + } + + /** + * Simple global function to run commands. + * + * @param string $command + * + * @return void + */ + public function quietlyAsUser(string $command) + { + $this->quietly('sudo -u "'.user().'" '.$command.' > /dev/null 2>&1'); + } + + public function transformCommandToRunAsUser(string $command, string $path = null): string + { + return ($path ? 'cd '.$path.' && ' : '').'sudo -u '.user().' '.$command; + } + + /** + * Pass the command to the command line and display the output. + * + * @param string $command + * + * @return void + */ + public function passthru(string $command) + { + passthru($command); + } + + /** + * Create a ProgressBar bound to this class instance + * + * @param int $count + * @param string $type + */ + public function createProgressBar(int $count, string $type = 'minimal'): void + { + $this->progress = new ProgressBar(new ConsoleOutput, $count); + + ProgressBar::setFormatDefinition('message', '%message% (%percent%%)'); + ProgressBar::setFormatDefinition('minimal', 'Progress: %percent%%'); + + $this->progress->setFormat($type); + $this->progress->setRedrawFrequency(25); + $this->progress->minSecondsBetweenRedraws(0.025); + $this->progress->maxSecondsBetweenRedraws(0.05); + } + + /** + * @param int|null $count + * + * @return \Symfony\Component\Console\Helper\ProgressBar + */ + public function getProgress(int $count = null): ProgressBar + { + if (!$this->progress instanceof ProgressBar) { + $this->createProgressBar($count); + } + + return $this->progress; + } + + /** + * Run the given command as the non-root user. + * + * @param string $command + * @param callable|null $onError + * @param string|null $workingDir + * + * @return string + */ + public function run(string $command, callable $onError = null, string $workingDir = null): string + { + return $this->runCommand($command, $onError, $workingDir); + } + + /** + * Run the given command. + * + * @param string $command + * @param callable|null $onError + * @param string|null $workingDir + * + * @return string + */ + public function runAsUser(string $command, callable $onError = null, string $workingDir = null): string + { + return $this->runCommand('sudo -u "'.user().'" '.$command, $onError, $workingDir); + } + + /** + * Run the given command. + * + * @param string $command + * @param callable|null $onError + * @param string|null $workingDir + * + * @return string + */ + public function runCommand(string $command, callable $onError = null, string $workingDir = null): string + { + info("Running Command: $command in $workingDir"); + $onError = $onError ? : function () {}; + + if (method_exists(Process::class, 'fromShellCommandline')) { + $process = Process::fromShellCommandline($command, null, $_ENV); + } else { + $process = new Process($command, null, $_ENV); + } + + if ($workingDir) { + if (is_dir($workingDir) && !is_file($workingDir)) { + $process->setWorkingDirectory($workingDir); + } + } + + $processOutput = ''; + + // $process->setTimeout(null)->run(function ($type, $line) use (&$processOutput) { + // $processOutput .= $line; + // }); + $process->setTimeout(null); + $process->start(); + foreach ($process as $type => $data) { + info($data); + $processOutput .= $data; + } + + if ($process->getExitCode() > 0) { + $onError($process->getExitCode(), $processOutput); + } + + return $processOutput; + } + + public function mustRun($command, $path = null) + { + return $this->runCommand($command, function($code, $output) { + throw new \Exception($output); + }, $path); + } +} diff --git a/pm4-tools/cli/ProcessMaker/Composer.php b/pm4-tools/cli/ProcessMaker/Composer.php new file mode 100644 index 0000000..08c1aae --- /dev/null +++ b/pm4-tools/cli/ProcessMaker/Composer.php @@ -0,0 +1,55 @@ +env('PACKAGES_PATH'); + if (!$path) { + $path = Install::read()['packages_path']; + } + $path = $this->handleTrailingSlash($path, $package); + return $path; + } + + public function codebasePath() + { + $path = $this->env('CODEBASE_PATH'); + if (!$path) { + $path = Install::read()['codebase_path']; + } + return $path; + } + + public function cachePath() + { + return $this->env('CACHE_PATH'); + } + + public function cacheManifestPath() + { + return $this->cachePath() . '/' . 'manifest.json'; + } + + public function codebaseEnv() + { + return Dotenv::createArrayBacked($this->codebasePath())->load(); + } + + public function env($var, $suppressError = false) + { + if (!isset($_ENV[$var])) { + if (!$suppressError) { + throw new \Exception("$var environment variable not found"); + } + return ""; + } + return $_ENV[$var]; + } + + public function handleTrailingSlash($path, $package) + { + if ($package) { + // if package is specified, add a trailing slash if it's missing + $path = rtrim($path, '/') . '/'; + $path = $path . $package; + } else { + // otherwise, make sure there is no trailing slash + $path = rtrim($path, '/'); + } + return $path; + } +} \ No newline at end of file diff --git a/pm4-tools/cli/ProcessMaker/FileSystem.php b/pm4-tools/cli/ProcessMaker/FileSystem.php new file mode 100644 index 0000000..62f0a54 --- /dev/null +++ b/pm4-tools/cli/ProcessMaker/FileSystem.php @@ -0,0 +1,387 @@ +isDir($path)) { + return false; + } + + $success = true; + + CommandLineFacade::runAsUser("rm -rf $path", + function ($error, $output) use (&$success, $path) { + $success = false; + }); + + return $success; + } + + /** + * Create a directory. + * + * @param string $path + * @param string|null $owner + * @param int $mode + * + * @return void + */ + public function mkdir(string $path, string $owner = null, int $mode = 0755): void + { + if (!mkdir($path, $mode, true) && ! is_dir($path)) { + throw new RuntimeException(sprintf('Directory "%s" was not created', $path)); + } + + if ($owner) { + $this->chown($path, $owner); + } + } + + /** + * Ensure that the given directory exists. + * + * @param string $path + * @param string|null $owner + * @param int $mode + * + * @return void + */ + public function ensureDirExists(string $path, string $owner = null, int $mode = 0755) + { + if (!$this->isDir($path)) { + $this->mkdir($path, $owner, $mode); + } + } + + /** + * Create a directory as the non-root user. + * + * @param string $path + * @param int $mode + * + * @return void + */ + public function mkdirAsUser(string $path, int $mode = 0755) + { + $this->mkdir($path, user(), $mode); + } + + /** + * Touch the given path. + * + * @param string $path + * @param string|null $owner + * + * @return string + */ + public function touch(string $path, string $owner = null): string + { + touch($path); + + if ($owner) { + $this->chown($path, $owner); + } + + return $path; + } + + /** + * Touch the given path as the non-root user. + * + * @param string $path + * + * @return string + */ + public function touchAsUser(string $path): string + { + return $this->touch($path, user()); + } + + /** + * Determine if the given file exists. + * + * @param string $path + * + * @return bool + */ + public function exists(string $path): bool + { + return file_exists($path); + } + + /** + * Read the contents of the given file. + * + * @param string $path + * + * @return string + */ + public function get(string $path): string + { + return file_get_contents($path); + } + + /** + * Write to the given file. + * + * @param string $path + * @param string $contents + * @param string|null $owner + * + * @return void + */ + public function put(string $path, string $contents, string $owner = null) + { + file_put_contents($path, $contents); + + if ($owner) { + $this->chown($path, $owner); + } + } + + /** + * Write to the given file as the non-root user. + * + * @param string $path + * @param string $contents + * + * @return void + */ + public function putAsUser(string $path, string $contents) + { + $this->put($path, $contents, user()); + } + + /** + * Append the contents to the given file. + * + * @param string $path + * @param string $contents + * @param string|null $owner + * + * @return void + */ + public function append(string $path, string $contents, string $owner = null) + { + file_put_contents($path, $contents, FILE_APPEND); + + if ($owner) { + $this->chown($path, $owner); + } + } + + /** + * Append the contents to the given file as the non-root user. + * + * @param string $path + * @param string $contents + * + * @return void + */ + public function appendAsUser(string $path, string $contents) + { + $this->append($path, $contents, user()); + } + + /** + * Copy the given file to a new location. + * + * @param string $from + * @param string $to + * + * @return void + */ + public function copy(string $from, string $to) + { + copy($from, $to); + } + + /** + * Copy the given file to a new location for the non-root user. + * + * @param string $from + * @param string $to + * + * @return void + */ + public function copyAsUser(string $from, string $to) + { + copy($from, $to); + + $this->chown($to, user()); + } + + /** + * Create a symlink to the given target. + * + * @param string $target + * @param string $link + * + * @return void + */ + public function symlink(string $target, string $link) + { + if ($this->exists($link)) { + $this->unlink($link); + } + + symlink($target, $link); + } + + /** + * Create a symlink to the given target for the non-root user. + * + * This uses the command line as PHP can't change symlink permissions. + * + * @param string $target + * @param string $link + * @return void + */ + public function symlinkAsUser($target, $link) + { + if ($this->exists($link)) { + $this->unlink($link); + } + + CommandLineFacade::runAsUser('ln -s '.escapeshellarg($target).' '.escapeshellarg($link)); + } + + /** + * Delete the file at the given path. + * + * @param string $path + * + * @return void + */ + public function unlink(string $path) + { + if (file_exists($path) || is_link($path)) { + @unlink($path); + } + } + + /** + * Change the owner of the given path. + * + * @param string $path + * @param string $user + */ + public function chown(string $path, string $user) + { + chown($path, $user); + } + + /** + * Change the group of the given path. + * + * @param string $path + * @param string $group + */ + public function chgrp(string $path, string $group) + { + chgrp($path, $group); + } + + /** + * Resolve the given path. + * + * @param string $path + * + * @return string + */ + public function realpath(string $path): string + { + return realpath($path); + } + + /** + * Determine if the given path is a symbolic link. + * + * @param string $path + * + * @return bool + */ + public function isLink(string $path): bool + { + return is_link($path); + } + + /** + * Resolve the given symbolic link. + * + * @param string $path + * + * @return string + */ + public function readLink(string $path): string + { + return readlink($path); + } + + /** + * Remove all of the broken symbolic links at the given path. + * + * @param string $path + * + * @return void + */ + public function removeBrokenLinksAt(string $path) + { + collect($this->scandir($path))->filter(function ($file) use ($path) { + return $this->isBrokenLink($path.'/'.$file); + })->each(function ($file) use ($path) { + $this->unlink($path.'/'.$file); + }); + } + + /** + * Determine if the given path is a broken symbolic link. + * + * @param string $path + * + * @return bool + */ + public function isBrokenLink(string $path): bool + { + return is_link($path) && ! file_exists($path); + } + + /** + * Scan the given directory path. + * + * @param string $path + * + * @return array + */ + public function scandir(string $path): array + { + return collect(scandir($path))->reject(function ($file) { + return in_array($file, ['.', '..']); + })->values()->all(); + } +} diff --git a/pm4-tools/cli/ProcessMaker/Git.php b/pm4-tools/cli/ProcessMaker/Git.php new file mode 100644 index 0000000..f9b5c94 --- /dev/null +++ b/pm4-tools/cli/ProcessMaker/Git.php @@ -0,0 +1,110 @@ +validateGitRepository($path_to_repo); + + $output = CommandLineFacade::runAsUser('git rev-parse --abbrev-ref HEAD', function ($e, $o) { + throw new RuntimeException('Error trying to retrieve current git branch name'); + }, $path_to_repo); + + return Str::replace([PHP_EOL, "\n"], '', $output); + } + + /** + * @param string $path_to_repo + * + * @return string + */ + public function getCurrentCommitHash(string $path_to_repo): string + { + $this->validateGitRepository($path_to_repo); + + $output = CommandLineFacade::runAsUser('git rev-parse --short HEAD', function ($e, $o) { + throw new RuntimeException('Error trying to retrieve current git commit hash.'); + }, $path_to_repo); + + return Str::replace([PHP_EOL, "\n"], '', $output); + } + + public function getDefaultBranch(string $path): string + { + $this->validateGitRepository($path); + + $command = "git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@'"; + + $branch = CommandLineFacade::runAsUser($command, function ($e, $o) { + warning('Could not find default git branch.'); + output($o); + }, $path); + + return Str::replace([PHP_EOL, "\n"], '', $branch); + } + + /** + * @param string $branchName + * @param string $path + * @param bool $force + * + * @return string + */ + public function switchBranch(string $branchName, string $path, bool $force = false): string + { + $this->validateGitRepository($path); + + if ($force) { + CommandLineFacade::runAsUser('git reset --hard', null, $path); + CommandLineFacade::runAsUser('git clean -d -f .', null, $path); + } + + $switched = CommandLineFacade::runAsUser("git checkout $branchName", function ($e, $o) { + throw new RuntimeException('Failed to switch branch'); + }, $path); + + return Str::replace([PHP_EOL, "\n"], '', $switched); + } + + public function shallowClone(string $package, string $branch = null, string $path, string $folderName = null) + { + $branchArg = ''; + if ($branch) { + $branchArg = "--branch $branch "; + } + + $token = ConfigFacade::env('GITHUB_TOKEN'); + CommandLineFacade::mustRun("mkdir -p $path"); + CommandLineFacade::mustRun("ls -A1 | xargs rm -rf", $path); + $cmd = "git clone --depth 1 ${branchArg}https://${token}@github.com/processmaker/${package} ${path}"; + CommandLineFacade::mustRun($cmd); + } +} diff --git a/pm4-tools/cli/ProcessMaker/Install.php b/pm4-tools/cli/ProcessMaker/Install.php new file mode 100644 index 0000000..b2e72fb --- /dev/null +++ b/pm4-tools/cli/ProcessMaker/Install.php @@ -0,0 +1,153 @@ +files = $files; + } + + /** + * Create the "sudoers.d" entry for running Valet. + * + * @return void + */ + public function createSudoersEntry(): void + { + $this->files->ensureDirExists('/etc/sudoers.d'); + + $this->files->put('/etc/sudoers.d/pm', 'Cmnd_Alias PM = '.BREW_PREFIX.'/bin/pm * +%admin ALL=(root) NOPASSWD:SETENV: PM'.PHP_EOL); + } + + /** + * Remove the "sudoers.d" entry for running Valet. + * + * @return void + */ + public function removeSudoersEntry() + { + CommandLineFacade::quietly('rm /etc/sudoers.d/pm'); + } + + public function symlinkToUsersBin(): void + { + $this->unlinkFromUsersBin(); + + CommandLineFacade::runAsUser('ln -s "'.realpath(__DIR__.'/../../pm').'" '.$this->bin); + } + + /** + * Remove the symlink from the user's local bin. + * + * @return void + */ + public function unlinkFromUsersBin(): void + { + CommandLineFacade::quietlyAsUser('rm '.$this->bin); + } + + /** + * Install the Valet configuration file. + * + * @param string $codebase_path + * @param string $packages_path + * + * @return void + */ + public function install(string $codebase_path, string $packages_path): void + { + $this->unlinkFromUsersBin(); + + $this->symlinkToUsersBin(); + + $this->createSudoersEntry(); + + $this->createConfigurationDirectory(); + + $this->write([ + 'codebase_path' => $codebase_path, + 'packages_path' => $packages_path + ]); + + $this->files->chown($this->path(), user()); + } + + /** + * Forcefully delete the Valet home configuration directory and contents. + * + * @return void + */ + public function uninstall(): void + { + $this->files->unlink(PM_HOME_PATH); + } + + /** + * Create the Valet configuration directory. + * + * @return void + */ + public function createConfigurationDirectory(): void + { + $this->files->ensureDirExists(PM_HOME_PATH, user()); + } + + /** + * Read the configuration file as JSON. + * + * @return array + */ + public function read(): array + { + return json_decode($this->files->get($this->path()), true); + } + + /** + * Update a specific key in the configuration file. + * + * @param string $key + * @param mixed $value + * + * @return array + */ + public function updateKey(string $key, $value): array + { + return tap($this->read(), function (&$config) use ($key, $value) { + $config[$key] = $value; + $this->write($config); + }); + } + + /** + * Write the given configuration to disk. + * + * @param array $config + * + * @return void + */ + public function write(array $config): void + { + $this->files->putAsUser($this->path(), json_encode( + $config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES).PHP_EOL + ); + } + + /** + * Get the configuration file path. + * + * @return string + */ + public function path(): string + { + return PM_HOME_PATH.'/config.json'; + } +} diff --git a/pm4-tools/cli/ProcessMaker/Packages.php b/pm4-tools/cli/ProcessMaker/Packages.php new file mode 100644 index 0000000..4bf34c4 --- /dev/null +++ b/pm4-tools/cli/ProcessMaker/Packages.php @@ -0,0 +1,547 @@ +getPackages())) { + throw new LogicException("Package with name \"$name\" does not exist locally."); + } + + return $this->getPackages()[$name]; + } + + /** + * @param string $name + * + * @return string + */ + public function getPackagePath(string $name): string + { + return $this->getPackage($name)['path']; + } + + /** + * @param bool $enterpriseOnly + * + * @return array + */ + public function getSupportedPackages(bool $enterpriseOnly = false): array + { + if (!$this->packageExists('packages')) { + $this->clonePackage('packages'); + } + + // We need the packages meta-package to get the + // list of supported enterprise packages that + // ProcessMaker 4 is compatible with + $packages_package = $this->getPackage('packages'); + $packages_package_path = $packages_package['path']; + + // Make sure we're on the right branch + $defaultBranch = GitFacade::getDefaultBranch($packages_package_path); + $branchSwitchResult = GitFacade::switchBranch($defaultBranch, $packages_package_path); + + // Find and decode composer.json + $composer_json = ComposerFacade::getComposerJson($packages_package_path); + + try { + // We want just the package names for now + $supported_packages = array_keys(get_object_vars($composer_json->extra->processmaker->enterprise)); + } catch (Exception $exception) { + throw new LogicException('Enterprise packages not found in processmaker/packages composer.json'); + } + + if (!$enterpriseOnly) { + // Merge the supported enterprise package names with + // the handful of other packages required for the + // primary (processmaker/processmaker) app to function + $supported_packages = array_merge($supported_packages ?? [], self::$additionalPackages); + } + + // Sort it and and remove two packages so they can be + // prepended as other packages rely on them if the order + // returned is the order installed + $supported_packages = collect($supported_packages)->values()->sort()->reject(static function ($package) { + return $package === 'docker-executor-node-ssr' + || $package === 'connector-send-email' + || $package === 'packages'; + }); + + // Prepend the removed packages so they're installed + // first, assuming the returned order is relied on + // for installation + return $supported_packages->prepend('connector-send-email') + ->prepend('docker-executor-node-ssr') + ->prepend('packages') + ->toArray(); + } + + /** + * @param string $name + * @param bool $force + * + * @return bool + */ + public function clonePackage(string $name, bool $force = false): bool + { + $name = Str::replace('processmaker/', '', $name); + + if (!$force && $this->packageExists($name)) { + throw new LogicException("Package already exists: processmaker/$name"); + } + + if ($force) { + FileSystemFacade::rmdir(ConfigFacade::packagesPath()."/$name"); + } + + $command = "git clone https://github.com/ProcessMaker/$name"; + + $output = CommandLineFacade::runAsUser($command, function ($code, $out) use ($name) { + throw new RuntimeException("Failed to clone $name: ".PHP_EOL.$out); + }, ConfigFacade::packagesPath()); + + return $this->packageExists($name); + } + + /** + * Clones all supported PM4 packages to the local package directory + * + * @param bool $force + * + * @return array + */ + public function cloneAllPackages(bool $force = false): array + { + // Clear the ProcessMaker packages directory before + // we start cloning the new ones down + if ($force) { + foreach ($this->getPackages() as $package) { + FileSystemFacade::rmdir($package['path']); + } + } + + // Clone down the processmaker/packages meta-package to + // to make sure we can reference all official supported + // ProcessMaker 4 enterprise packages + if (!$this->packageExists('packages')) { + $this->clonePackage('packages'); + } + + return $this->getSupportedPackages(); + } + + /** + * @param string $name + * + * @return bool + */ + public function packageExists(string $name): bool + { + if (Str::contains($name, 'processmaker/')) { + $name = Str::replace('processmaker/', '', $name); + } + + return in_array($name, $this->getPackagesListFromDirectory(), true); + } + + /** + * Get the names of the local composer packages + * + * @param string|null $package_directory + * + * @return array + */ + public function getPackagesListFromDirectory(string $package_directory = null): array + { + if (!is_string($package_directory)) { + $package_directory = ConfigFacade::packagesPath(); + } + + return array_filter(FileSystemFacade::scandir($package_directory), function ($dir) use($package_directory) { + + // Set the absolute path to the file or directory + $dir = $package_directory.'/'.$dir; + + // Filter out any non-directory files + return FileSystemFacade::isDir($dir) && !is_file($dir); + }); + } + + /** + * Returns an multi-dimensional array with + * each package name and path + * + * @return array + */ + public function getPackages(): array + { + $packages = array_map(function ($package_name) { + return [ + 'name' => $package_name, + 'path' => ConfigFacade::packagesPath() . "/$package_name" + ]; + }, $this->getPackagesListFromDirectory()); + + return collect($packages)->keyBy('name')->toArray(); + } + + /** + * Get the package version number for a package + * + * @param string $package_directory + * + * @return string + */ + public function getPackageVersion(string $package_directory): string + { + $composer_json = ComposerFacade::getComposerJson($package_directory) ?? new class {}; + + if (!property_exists($composer_json, 'version')) { + return '...'; + } + + return $composer_json->version; + } + + /** + * @param string $path + * + * @return string + */ + public function getCurrentGitBranchName(string $path): string + { + if (!FileSystemFacade::isDir($path)) { + return '...'; + } + + // Run this command and get the current git branch + $branch = CommandLineFacade::runAsUser('git rev-parse --abbrev-ref HEAD', null, $path); + + // Remove unnecessary end of line character(s) + return Str::replace(["\n", PHP_EOL], "", $branch); + } + + /** + * @param bool $verbose + * + * @throws \Illuminate\Contracts\Container\BindingResolutionException + */ + public function pull41(bool $verbose = false) + { + $this->pull($verbose, '4.1-develop'); + } + + /** + * Stores basic package metadata on this class instance. Used to store metadata + * prior to running the pull() method for comparisons afterwards. + * + * @param bool $updated + * @param array $metadata Any package metadata to start with + * + * @return array + */ + public function takePackagesSnapshot(bool $updated = false, array $metadata = []): array + { + foreach ($this->getPackages() as $package) { + + $path = $package['path']; + $version_key = $updated ? 'updated_version' : 'version'; + $branch_key = $updated ? 'updated_branch' : 'branch'; + $hash_key = $updated ? 'updated_commit_hash' : 'commit_hash'; + + $metadata[$package['name']] = [ + 'name' => $package['name'], + $version_key => $this->getPackageVersion($path), + $branch_key => $this->getCurrentGitBranchName($path), + $hash_key => GitFacade::getCurrentCommitHash($path) + ]; + } + + return $metadata; + } + + /** + * @param string $branch + * @param array $commands + * + * @return array + */ + public function buildPullCommands(string $branch, array $commands = []): array + { + foreach ($this->getPackages() as $package) { + $package_commands = [ + 'git reset --hard', + 'git clean -d -f .', + "git checkout $branch", + 'git fetch --all', + 'git pull --force', + ]; + + $commands[$package['name']] = array_map(function ($command) use ($package) { + return CommandLineFacade::transformCommandToRunAsUser($command, $package['path']); + }, $package_commands); + } + + return $commands; + } + + /** + * @param bool $verbose + * @param string|null $branch + * + * @throws \Illuminate\Contracts\Container\BindingResolutionException + */ + public function pull(bool $verbose = false, string $branch = null): void + { + // A quick command (thanks Nolan!) to grab the default branch + $get_default_git_branch = "$(git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@')"; + + // Build the commands for each package (keyed by package name) + $commands = $this->buildPullCommands($branch ?? $get_default_git_branch); + + // Store the pre-pull metadata for each package + $metadata = $this->takePackagesSnapshot(); + + // Create a new ProcessManagerFacade instance to run the + // git commands in parallel where possible + $processManager = resolve(ProcessManager::class); + + // Set verbosity for output to stdout + $processManager->setVerbosity($verbose); + + // Set a closure to be called when the final process exits + $processManager->setFinalCallback(function () use ($metadata) { + $this->outputPullResults($metadata); + }); + + // Build the process queue and run + $processManager->buildProcessesBundleAndStart($commands); + } + + /** + * @return array + */ + public function getPackagesTableData(): array + { + $table = []; + + // Build the table rows by merging the compare-with + // package metadata with a recent snapshot + foreach ($this->takePackagesSnapshot() as $package => $updated) { + $table[$package] = $updated; + } + + // Sort the columns in a more sensible way + foreach ($table as $key => $row) { + $table[$key] = [ + 'name' => ''.$row['name'].'', + 'version' => $row['version'], + 'branch' => $row['branch'], + 'commit_hash' => $row['commit_hash'], + ]; + } + + return $table; + } + + /** + * @param array $pre_pull_package_metadata + */ + public function outputPullResults(array $pre_pull_package_metadata) + { + $table = []; + + // Build the table rows + foreach ($this->takePackagesSnapshot(true) as $package => $updated) { + $table[$package] = array_merge($pre_pull_package_metadata[$package], $updated); + } + + // Sort the columns in a more sensible way + foreach ($table as $key => $row) { + $table[$key] = [ + 'name' => $row['name'], + 'version' => $row['version'], + 'updated_version' => $row['updated_version'], + 'branch' => $row['branch'], + 'updated_branch' => $row['updated_branch'], + 'commit_hash' => $row['commit_hash'], + 'updated_commit_hash' => $row['updated_commit_hash'] + ]; + } + + // Add console styling + foreach ($table as $key => $row) { + + // Highlight the package name + $table[$key]['name'] = ''.$row['name'].''; + + // If the versions are the same, no updated occurred. + // If they are different, let's make it easier to see. + if ($row['version'] !== $row['updated_version']) { + $table[$key]['updated_version'] = ''.$row['updated_version'].''; + } + + // Do the same thing with branches, since we may + // have switch to 4.1 or 4.2 during the pull, which + // is set by the user by adding a flag to the command + if ($row['branch'] !== $row['updated_branch']) { + $table[$key]['updated_branch'] = ''.$row['updated_branch'].''; + } + + // One more time to see if the commit hash has changed + if ($row['commit_hash'] !== $row['updated_commit_hash']) { + $table[$key]['updated_commit_hash'] = ''.$row['updated_commit_hash'].''; + } + } + + // Add a new line for space above the table + output(PHP_EOL); + + // Format our results in an easy-to-ready table + table(['Name', 'Version ->', '-> Version', 'Branch ->', '-> Branch', 'Hash ->', '-> Hash'], $table); + } + + /** + * Build the stack of commands to composer require and + * install each enterprise ProcessMaker 4 package + * + * @param bool $for_41_develop + * @param bool $force + * + * @return \Illuminate\Support\Collection + */ + public function buildPackageInstallCommands(bool $for_41_develop = false, bool $force = false): Collection + { + if (!FileSystemFacade::isDir(ConfigFacade::codebasePath())) { + throw new LogicException('Could not find ProcessMaker codebase: '. ConfigFacade::codebasePath()); + } + + // Find out which branch to switch to in the local + // processmaker/processmaker codebase + $branch = $for_41_develop ? '4.1-develop' : 'develop'; + + // Find out which branch we're on + $current_branch = GitFacade::getCurrentBranchName(ConfigFacade::codebasePath()); + + // Make sure we're on the right branch + if ($current_branch !== $branch && ! $force) { + throw new DomainException("Core codebase branch should be \"$branch\" but \"$current_branch\" was found."); + } + + // Grab the list of supported enterprise packages + $enterprise_packages = new Collection($this->getSupportedPackages(true)); + + if ($for_41_develop) { + // Filter out any packages not on the 4.1-develop + // branch which aren't compatible with 4.1 + $enterprise_packages = $enterprise_packages->reject(function ($package) { + if ($package === 'docker-executor-node-ssr') { + return false; + } + + return '4.1-develop' !== GitFacade::getCurrentBranchName($this->getPackagePath($package)); + }); + } + + // Key by package name + $enterprise_packages = $enterprise_packages->keyBy(static function ($package, $index) { + return $package; + }); + + // Build the stack of commands to run + return $enterprise_packages->transform(static function (string $package) { + return new Collection([ + "composer require processmaker/$package --no-interaction", + PHP_BINARY." artisan $package:install --no-interaction", + PHP_BINARY." artisan vendor:publish --tag=$package --no-interaction" + ]); + }); + } + + public function getEnterprisePackages($tests = false) + { + // TODO: Bring this part back when all unit test fixes are merged + $composer = FileSystemFacade::get(ConfigFacade::codebasePath() . '/composer.json'); + $composer = json_decode($composer, true); + $list = Arr::get($composer, 'extra.processmaker.enterprise', []); + + return collect(array_keys($list)); + + // $list = collect(require(__DIR__ . '/../../../packages-to-install.php')); + // if ($tests) { + // $list = $list->filter(fn($p) => $p['tests'] === true); + // } + + // $list = $list->keys(); + + // // Always install the current package + // if (PackagesCi::repoName() !== 'processmaker') { + // $list->push(PackagesCi::repoName()); + // } + + // // Always install packages listed in the pr body + // foreach(PackagesCi::getBranches() as $key => $branch) { + // if ($key !== 'processmaker') { + // $list->push($key); + // } + // } + + // return $list; + } + + public function getJavascriptPackages($path) + { + $composer = FileSystemFacade::get($path . '/package.json'); + $composer = json_decode($composer, true); + $list = Arr::get($composer, 'dependencies', []); + return collect(array_keys($list))->filter(function($package) { + return Str::startsWith($package, '@processmaker/'); + })->map(function($package) { + return str_replace('@processmaker/', '', $package); + })->filter(function($package) { + return PackagesCi::getBranch($package); + }); + } +} diff --git a/pm4-tools/cli/ProcessMaker/PackagesCi.php b/pm4-tools/cli/ProcessMaker/PackagesCi.php new file mode 100644 index 0000000..13972fb --- /dev/null +++ b/pm4-tools/cli/ProcessMaker/PackagesCi.php @@ -0,0 +1,423 @@ + 'npm run build-bundle', + "screen-builder" => 'npm run build-bundle', + "vue-form-elements" => 'npm run build-bundle', + ]; + + private $cacheManifest; + private $remoteShas = []; + + public function build() + { + $this->buildCore(); + $this->buildPackages(); + } + + public function buildJavascript() + { + $javascriptPackages = Packages::getJavascriptPackages(Config::codebasePath()); + $this->installJavascriptPackages($javascriptPackages); + $this->npmRunDev(); + } + + public function install() + { + $this->installCore(); + $list = Packages::getEnterprisePackages(); + + // Our packages-to-install.php is ordered + $list = $this->orderList($list); + + foreach ($list as $package) { + info("Artisan installing $package"); + $this->artisanInstall($package); + } + + $this->artisanCommand('optimize:clear'); + $this->artisanCommand('horizon:terminate'); + + info("Modifying phpunit.xml to add package tests"); + PhpUnit::addTests(PhpUnit::configFile()); + // $this->exportDatabase(); + + $this->cleanUp(); + + info("Done"); + } + + public function cleanUp() + { + CommandLine::mustRun('rm -rf node_modules', Config::codebasePath()); + } + + public function shouldUseCache($package) + { + info("Checking cache for package $package"); + return $this->remoteSha($package) === $this->cachedSha($package); + } + + public function fetchBranchSha($package, $branch = null) + { + if (!$branch) { + $branch = 'HEAD'; + } + + $token = Config::env('GITHUB_TOKEN'); + $sha = CommandLine::mustRun("git ls-remote https://${token}@github.com/processmaker/$package $branch | awk '{ print $1}'"); + return trim($sha); + } + + public function setCache($package) + { + if ($package === 'processmaker') { + $from = Config::codebasePath(); + } else { + $from = Config::packagesPath($package); + } + $to = Config::cachePath() . '/' . $package; + $this->copy($from, $to); + $this->updateManifest($package); + } + + public function restoreCache($package) + { + if ($package === 'processmaker') { + $to = Config::codebasePath(); + } else { + $to = Config::packagesPath($package); + } + $from = Config::cachePath() . '/' . $package; + $this->copy($from, $to); + } + + public function copy($from, $to) + { + CommandLine::mustRun("mkdir -p $to"); + CommandLine::mustRun("ls -A1 | xargs rm -rf", $to); + CommandLine::mustRun("rsync -r --exclude='/node_modules' --exclude='/vendor' ${from}/ ${to}"); + } + + private function cachedSha($package) + { + $manifest = $this->getCacheManifest(); + if (isset($manifest[$package])) { + return $manifest[$package]; + } + return null; + } + + private function remoteSha($package) + { + if (isset($this->remoteShas[$package])) { + $sha = $this->remoteShas[$package]; + } else { + $sha = $this->fetchBranchSha($package, $this->getBranch($package)); + $this->remoteShas[$package] = $sha; + } + return $sha; + } + + private function getCacheManifest() + { + if ($this->cacheManifest) { + return $this->cacheManifest; + } + $manifest = []; + if (FileSystem::exists(Config::cacheManifestPath())) { + $manifest = json_decode(FileSystem::get(Config::cacheManifestPath()), true); + } + $this->cacheManifest = $manifest; + return $manifest; + } + + private function updateManifest($package) + { + $manifest = $this->getCacheManifest(); + $manifest[$package] = $this->remoteSha($package); + $this->cacheManifest = $manifest; + $manifest = json_encode($manifest); + FileSystem::put(Config::cacheManifestPath(), $manifest); + } + + private function composerRequireList($list) + { + return $list->map(function ($package) { + return "processmaker/$package"; + })->join(" "); + } + + private function artisanInstall($package) + { + return $this->artisanCommand("${package}:install"); + } + + private function artisanCommand($cmd) + { + return CommandLine::runCommand("php artisan ${cmd}", function ($code, $output) { + throw new \Exception($output); + }, Config::codebasePath()); + } + + public function getBranch($package) + { + if (!$this->branches) { + $this->setBranches(); + } + if (isset($this->branches[$package])) { + return $this->branches[$package]; + } + + return null; + } + + private function setBranches() + { + $result = preg_match_all('/ci:(.+?):(.+?)(\s|$)/', $this->pullRequestBody(), $matches); + if ($result && $result > 0) { + $this->branches = array_combine($matches[1], $matches[2]); + } else { + $this->branches = []; + } + $this->branches[$this->repoName()] = $this->pullRequestBranch(); + } + + public function getBranches() + { + if (!$this->branches) { + $this->setBranches(); + } + return $this->branches; + } + + private function pullRequestBody() + { + return Config::env('CI_PR_BODY'); + } + + private function pullRequestBranch() + { + return Config::env('CI_PACKAGE_BRANCH'); + } + + public function repoName() + { + return Config::env('CI_PROJECT'); + } + + private function installCommand() + { + extract($_ENV); + if ($PM_APP_PORT !== "80") { + $portWithPrefix = ":${PM_APP_PORT}"; + } + + return <<cloneCore(); + CommandLine::mustRun('composer install --no-interaction', Config::codebasePath()); + } + + private function buildPackages() + { + info("Installing enterprise packages for CI"); + $list = Packages::getEnterprisePackages(); + + info("Cloning " . $list->count() . " packages"); + foreach ($list as $package) { + $branch = $this->getBranch($package); + $info = "Cloning $package"; + if ($branch) { + $info .= " with branch $branch"; + } + info($info); + Git::shallowClone($package, $branch, Config::packagesPath($package)); + } + + foreach ($list as $package) { + info("Registering local repository path for $package"); + Composer::addRepositoryPath($package); + } + + info("Composer requiring packages"); + $listString = $this->composerRequireList($list); + Composer::require($listString); + } + + private function installCore() + { + $this->createDatabase(Config::env('DB_DATABASE')); + CommandLine::mustRun($this->installCommand(), Config::codebasePath()); + FileSystem::append(Config::codebasePath() . '/.env', $this->additionalEnv()); + } + + private function cloneCore() + { + $pm = 'processmaker'; + info("Cloning processmaker core"); + Git::shallowClone($pm, $this->getBranch($pm), Config::codebasePath()); + } + + public function databaseConnectionParams() + { + extract($_ENV); + return "-h $DB_HOSTNAME -P $DB_PORT -u $DB_USERNAME -p'${DB_PASSWORD}'"; + } + + public function createDatabase($name) + { + $connection = $this->databaseConnectionParams(); + CommandLine::mustRun("mysql $connection -e 'DROP DATABASE IF EXISTS `$name`;'"); + CommandLine::mustRun("mysql $connection -e 'CREATE DATABASE `$name`;'"); + } + + public function exportDatabase() + { + $from = Config::env('DB_DATABASE'); + $to = 'test'; + $file = 'database.sql'; + $connection = $this->databaseConnectionParams(); + CommandLine::mustRun("mysqldump $connection $from > $file"); + $this->createDatabase($to); + CommandLine::mustRun("mysql $connection $to < $file"); + } + + private function clonePackage($package) + { + Git::shallowClone($package, $this->getBranch($package), Config::packagesPath($package)); + } + + private function installJavascriptPackages($packages) + { + $packagesToBuild = []; + foreach ($packages as $package) { + // Only clone/build/link if specified in the PR + // Otherwise, just use core's package.json + if (!$this->getBranch($package)) { + continue; + } + + $path = Config::packagesPath($package); + $this->clonePackage($package); + + // unsafe-perm is needed to run postinstall scripts as root + CommandLine::mustRun("npm install --unsafe-perm", $path); + $packagesToBuild[] = $package; + } + + foreach ($packagesToBuild as $package) { + $this->linkDependentPackages(Config::packagesPath($package)); + $this->buildJavascriptPackage($package); + } + + return count($packagesToBuild); + } + + private function linkDependentPackages($path) + { + $dependentPackages = Packages::getJavascriptPackages($path); + foreach ($dependentPackages as $dependentPackage) { + CommandLine::mustRun("mkdir -p node_modules/@processmaker", $path); + $packagePath = Config::packagesPath($dependentPackage); + $nodeModulesPackagePath = "node_modules/@processmaker/${dependentPackage}"; + CommandLine::mustRun("rm -rf $nodeModulesPackagePath", $path); + CommandLine::mustRun("ln -s $packagePath $nodeModulesPackagePath", $path); + } + } + + private function buildJavascriptPackage($package) + { + if (isset($this->javascriptPackageBuildCommands[$package])) { + $cmd = $this->javascriptPackageBuildCommands[$package]; + CommandLine::mustRun($cmd, Config::packagesPath($package)); + } + CommandLine::mustRun('rm -rf node_modules', Config::packagesPath($package)); + } + + private function npmRunDev() + { + // unsafe-perm is needed to run postinstall scripts as root + info("Starting at " . date('c')); + CommandLine::mustRun('npm ci --unsafe-perm --omit=dev', Config::codebasePath()); + $this->linkDependentPackages(Config::codebasePath()); + CommandLine::mustRun('npm install cross-env', Config::codebasePath()); + CommandLine::mustRun('npm run dev', Config::codebasePath()); + info("Finished at " . date('c')); + } + + private function orderList($list) + { + return $list->sortBy(function ($package) { + switch($package) { + // Must be installed first + case 'package-data-vocabularies': + return 0; + + // Must be installed last + case 'connector-docusign': + return 2; + + default: + return 1; + } + }); + } +} diff --git a/pm4-tools/cli/ProcessMaker/PhpUnit.php b/pm4-tools/cli/ProcessMaker/PhpUnit.php new file mode 100644 index 0000000..e16bb2c --- /dev/null +++ b/pm4-tools/cli/ProcessMaker/PhpUnit.php @@ -0,0 +1,45 @@ +preserveWhiteSpace = false; + $dom->formatOutput = true; + $dom->load($path); + + $xpath = new DOMXpath($dom); + $directories = $xpath->query('//testsuite[@name="Features"]')[0]; + + foreach ($list as $package) + { + $testsDirectory = Config::packagesPath($package) . "/tests"; + if (FileSystem::exists($testsDirectory)) { + info("Phpunit - Adding $testsDirectory"); + $directory = $dom->createElement('directory', $testsDirectory); + $directories->appendChild($directory); + } + } + + return $dom->save($path); + } + + public function configFile() + { + return Config::codebasePath() . '/phpunit.xml'; + } + +} \ No newline at end of file diff --git a/pm4-tools/cli/ProcessMaker/ProcessManager.php b/pm4-tools/cli/ProcessMaker/ProcessManager.php new file mode 100644 index 0000000..fb2895c --- /dev/null +++ b/pm4-tools/cli/ProcessMaker/ProcessManager.php @@ -0,0 +1,292 @@ +cli = $cli; + $this->processCollections = $processCollections; + $this->outputCollection = $outputCollection; + } + + public function setVerbosity(bool $verbose) + { + $this->verbose = $verbose; + } + + public function getProcessOutput(string $key = null): Collection + { + return $key ? $this->outputCollection->get($key) : $this->outputCollection; + } + + public function addProcessOutput(string $key, $output) + { + if (!$this->getProcessOutput()->has($key)) { + $this->getProcessOutput()->put($key, new Collection); + } + + $this->getProcessOutput($key)->push($output); + } + + public function findProcessExitCode(string $key): int + { + if (!$output = $this->getProcessOutput($key)) { + return 1; + } + + // Search through the output for the array + // containing the exit code value + $exitCode = $output->reject(function ($line) { + return ! is_array($line) || ! array_key_exists('exit_code', $line); + }); + + // If we can't find it, assume the process + // exited with a general error + if ($exitCode->isNotEmpty()) { + return $exitCode->flatten()->first() ?? 1; + } + + return 1; + } + + /** + * @param string $key + * + * @return Collection + */ + public function getProcessCollections(string $key): Collection + { + if (!$this->processCollections->get($key) instanceof Collection) { + $this->processCollections->put($key, new Collection()); + } + + return $this->processCollections->get($key); + } + + /** + * @param array $commands + */ + public function buildProcessesBundleAndStart(array $commands) + { + $this->startProcessesBundle($this->buildProcessesBundle($commands)); + } + + /** + * @param array $commands + * + * @return \Illuminate\Support\Collection + */ + public function buildProcessesBundle(array $commands): Collection + { + $commands = array_filter($commands, function ($command) { + return !is_string($command); + }); + + if (blank($commands)) { + throw new LogicException('Commands array cannot be empty'); + } + + $bundles = collect($commands)->transform(function (array $set) { + return collect(array_map(function ($command) { + return new Process($command); + }, $set)); + }); + + return $this->setExitListeners($bundles); + } + + /** + * @param \Illuminate\Support\Collection $bundles + * + * @return \Illuminate\Support\Collection + */ + private function setExitListeners(Collection $bundles): Collection + { + // Set each Process to start the next process + // in the bundle when it exists + $bundles->each(function (Collection $bundle) { + return $bundle->transform(function (Process $process, $index) use (&$bundle) { + + $process->on('exit', function ($exitCode, $termSignal) use (&$bundle, $process, $index) { + + if (!$this->verbose) { + $this->cli->getProgress()->advance(); + } + + // Add to the "exited" process collection + $this->getProcessCollections('exited')->push($process); + + // Get the info we need to output to stdout + $pid = $process->getPid(); + $command = $process->getCommand(); + + if ($this->verbose) { + if ($exitCode === 0) { + output("$pid: $command"); + } else { + output("$pid: $command"); + } + } + + // Add to the process collections + if ($exitCode === 0) { + $this->getProcessCollections('successful')->push($process); + } else { + $this->getProcessCollections('errors')->push($process); + } + + // Add to the processOutput property for reading later + $this->addProcessOutput($process->getCommand(), ['exit_code' => $exitCode]); + + // Find the next process to run + $next_process = $bundle->get($index + 1); + + // If one exists, run it + if ($next_process instanceof Process) { + $this->startProcessAndPipeOutput($next_process); + } + + $queued = $this->getProcessCollections('queued')->count(); + $exited = $this->getProcessCollections('exited')->count(); + + // All processes are finished + if ($queued === $exited) { + // Keeps the stdout clean during verbose mode + if (!$this->verbose) { + $this->cli->getProgress()->finish(); + } + + // Last but not least, run the bound callback + $this->getFinalCallback(); + } + }); + + return $process; + }); + }); + + return $bundles; + } + + public function getFinalCallback() + { + if (is_callable($this->finalCallback)) { + return call_user_func($this->finalCallback); + } + } + + public function setFinalCallback(callable $callback) + { + $this->finalCallback = $callback; + } + + /** + * @param \Illuminate\Support\Collection $bundle + * + * @return \Illuminate\Support\Collection + */ + private function validateBundle(Collection $bundle): Collection + { + return $bundle->reject(function ($process) { + return ! $process instanceof Process; + }); + } + + /** + * @param \Illuminate\Support\Collection $bundles + */ + public function startProcessesBundle(Collection $bundles): void + { + $bundles->transform(function (Collection $bundle) { + return $this->validateBundle($bundle); + }); + + if ($bundles->isEmpty()) { + throw new LogicException('No bundles of Processes found'); + } + + $this->setProcessIndexes($bundles); + + $this->getStartProcesses($bundles)->each(function (Process $process) { + $this->startProcessAndPipeOutput($process); + }); + } + + /** + * @param \Illuminate\Support\Collection $bundles + * + * @return \Illuminate\Support\Collection + */ + private function getStartProcesses(Collection $bundles): Collection + { + return $this->validateBundle( + $bundles->map(function (Collection $bundle) { + return $bundle->first(); + }) + ); + } + + /** + * @param \Illuminate\Support\Collection $bundles + * + * @return void + */ + private function setProcessIndexes(Collection $bundles): void + { + $index = 0; + $total_processes = 0; + + // Count up all of the processes + $bundles->each(function ($bundle) use (&$total_processes) { + $total_processes += $bundle->count(); + }); + + // Set the "process_index" property for each + // process among each bundle + $bundles->each(function ($bundle) use (&$index) { + $bundle->transform(function (Process $process) use (&$index) { + $process->index = $index++; + + $this->getProcessCollections('queued')->push($process); + + return $process; + }); + }); + + if (!$this->verbose) { + $this->cli->getProgress($total_processes); + } + } + + /** + * @param \React\ChildProcess\Process $process + */ + private function startProcessAndPipeOutput(Process $process): void + { + if ($process->isRunning()) { + return; + } + + $process->start(); + + $process->stdout->on('data', function ($output) use (&$process) { + $this->addProcessOutput($process->getCommand(), $output); + }); + + $this->getProcessCollections('started')->push($process); + } +} diff --git a/pm4-tools/cli/pm.php b/pm4-tools/cli/pm.php new file mode 100644 index 0000000..2dd9b8d --- /dev/null +++ b/pm4-tools/cli/pm.php @@ -0,0 +1,294 @@ +#!/usr/bin/env php +load(); +} + +Container::setInstance(new Container); + +$app = new Application('ProcessMaker CLI Tool', '0.5.0'); + +if (!FileSystem::isDir(PM_HOME_PATH)) { + /* + * -------------------------------------------------+ + * | | + * | Command: Install Cli | + * | | + * -------------------------------------------------+ + */ + $app->command('install-cli', function (InputInterface $input, OutputInterface $output) { + + // First thing we want to do is ask the user to + // tell us the absolute path to the core codebase + $helper = $this->getHelperSet()->get('question'); + + // Callback to autocomplete the directories available + // as the user is typing + $callback = function (string $userInput): array { + + $inputPath = preg_replace('%(/|^)[^/]*$%', '$1', $userInput); + $inputPath = '' === $inputPath ? '.' : $inputPath; + $foundFilesAndDirs = @scandir($inputPath) ?: []; + + return array_map(static function ($dirOrFile) use ($inputPath) { + return $inputPath.$dirOrFile; + }, $foundFilesAndDirs); + }; + + $question = "Please enter the absolute path to the local copy of the processmaker/processmaker codebase:".PHP_EOL; + $question = new Question($question); + $question->setAutocompleterCallback($callback); + $codebase_path = $helper->ask($input, $output, $question); + + // Make sure they entered something + if (null === $codebase_path) { + warningThenExit('You must enter a valid absolute path to continue the installation. Please try again.'); + } + + // Check for composer.json + if (!FileSystem::exists("$codebase_path/composer.json")) { + warningThenExit("Could not the composer.json for processmaker/processmaker in: $codebase_path"); + } + + // Next we need to know where all of the local copies + // of the processmaker/* packages will be stored + $question = "Please enter the absolute path to the directory where local copies of the ProcessMaker packages will be stored::".PHP_EOL; + $question = new Question($question); + $question->setAutocompleterCallback($callback); + $packages_path = $helper->ask($input, $output, $question); + + // Make sure they entered something + if (null === $packages_path) { + warningThenExit('You must enter a valid absolute path to continue the installation. Please try again.'); + } + + // Creates the sudoers entry and the base config file/directory + Install::install($codebase_path, $packages_path); + + info('Installation complete!'); + + })->descriptions('Runs the installation process for this tool. Necessary before other commands will appear.'); + +} else { + + /* + * -------------------------------------------------+ + * | | + * | Command: Install Packages | + * | | + * -------------------------------------------------+ + */ + $app->command('install-packages [-4|--for_41_develop]', function (InputInterface $input, OutputInterface $output) { + + // Indicates if we should install the 4.1-develop + // versions of each package or the 4.2 + $for_41_develop = $input->getOption('for_41_develop'); + + // Should the output be verbose or not + $verbose = $input->getOption('verbose'); + + // Use an anonymous function to we can easily re-run if + // we decide to force the installation of the packages + $build_install_commands = static function ($force = false) use ($for_41_develop) { + return Packages::buildPackageInstallCommands($for_41_develop, $force); + }; + + // Builds an array of commands to run in the local + // processmaker/processmaker codebase to require + // each supported package, then install it and + // publish it's vendor assets (if any are available) + try { + $install_commands = $build_install_commands(); + } catch (DomainException $exception) { + + // Show the user the incompatible branch information + warning($exception->getMessage()); + + // Ask the user if they want to force the install anyway + $helper = $this->getHelperSet()->get('question'); + $question = new ConfirmationQuestion('Force install the packages? "No" to abort or "Yes" to proceed: ', false); + + // Bail out if they user doesn't want to force the install + if (false === $helper->ask($input, $output, $question)) { + warningThenExit('Packages install aborted.'); + } + + // Re-run the install but add the force argument to + // prevent the incompatible exception from being thrown + $install_commands = $build_install_commands(true); + } + + // Grab an instance of the CommandLine class + $cli = resolve(\ProcessMaker\Cli\CommandLine::class); + + // Create a progress bar and start it + $cli->createProgressBar($install_commands->flatten()->count(), 'message'); + + // Set the initial message and start up the progress bar + $cli->getProgress()->setMessage('Starting install...'); + $cli->getProgress()->start(); + + // Iterate through the collection of commands + foreach ($install_commands as $package => $command_collection) { + + // Iterate through each command and attempt to run it + foreach ($command_collection as $command) { + + // Update the progress bar + $cli->getProgress()->setMessage("Installing $package..."); + $cli->getProgress()->advance(); + + try { + $command_output = $cli->runAsUser($command, static function ($exitCode, $out) { + throw new RuntimeException($out); + }, Config::codebasePath()); + } catch (RuntimeException $exception) { + + $cli->getProgress()->clear(); + + output("Command Failed: $command"); + output($exception->getMessage()); + + $cli->getProgress()->display(); + + continue; + } + + if (!$verbose) { + continue; + } + + // If the user wants verbose output, then show the + // stdout for all of the successful commands as well + // (in addition to the ones which failed) + $cli->getProgress()->clear(); + + output("Command Success: $command"); + output($command_output); + + $cli->getProgress()->display(); + } + } + + // Clean up the progress bar + $cli->getProgress()->finish(); + $cli->getProgress()->clear(); + + // See how long it took to run everything + $timing = $cli->timing(); + + // Output and we're done! + output(PHP_EOL."Finished in $timing"); + + })->descriptions('Installs all enterprise packages in the local ProcessMaker core (processmaker/processmaker) codebase.', [ + '--for_41_develop' => 'Uses 4.1 version of the supported packages' + ]); + + /* + * -------------------------------------------------+ + * | | + * | Command: Packages | + * | | + * -------------------------------------------------+ + */ + $app->command('packages', function () { + + table(['Name', 'Version', 'Branch', 'Commit Hash'], Packages::getPackagesTableData()); + + })->descriptions('Display the current version, branch, and names of known local packages'); + + /* + * -------------------------------------------------+ + * | | + * | Command: Pull | + * | | + * -------------------------------------------------+ + */ + $app->command('pull [-4|--for_41_develop]', function (InputInterface $input, OutputInterface $output) { + + // Updates to 4.1-branch of packages (or not) + $for_41_develop = $input->getOption('for_41_develop'); + + // Set verbosity level of output + $verbose = $input->getOption('verbose'); + + // Put everything together and run it + $for_41_develop ? Packages::pull41($verbose) : Packages::pull($verbose); + + })->descriptions('Cycles through each local store of supported ProcessMaker 4 packages.', + ['--for_41_develop' => 'Change each package to the correct version for the 4.1 version of processmaker/processmaker'] + ); + + /* + * -------------------------------------------------+ + * | | + * | Command: Clone All | + * | | + * -------------------------------------------------+ + */ + $app->command('clone-all [-f|--force]', function ($force = null) { + foreach (Packages::getSupportedPackages() as $index => $package) { + try { + if (Packages::clonePackage($package)) { + info("Package $package cloned successfully!"); + } + } catch (Exception $exception) { + warning($exception->getMessage()); + } + } + })->descriptions('Clone all supported ProcessMaker 4 packages to a local directory', [ + '--force' => 'Delete the package locally if it exists already' + ]); + +} + +/* +* -------------------------------------------------+ +* | | +* | Command: Install Packages CI | +* | | +* -------------------------------------------------+ +*/ +$app->command('build-ci', function() { + PackagesCi::build(); +}); + +$app->command('build-javascript-ci', function() { + PackagesCi::buildJavascript(); +}); + +$app->command('install-ci', function() { + PackagesCi::install($this); +}); + +$app->run(); diff --git a/pm4-tools/composer.json b/pm4-tools/composer.json new file mode 100644 index 0000000..b8567fa --- /dev/null +++ b/pm4-tools/composer.json @@ -0,0 +1,41 @@ +{ + "name": "processmaker/pm4-tools", + "description": "A lightweight cli tool to aid local develop of ProcessMaker software", + "type": "project", + "license": "MIT", + "bin": ["pm"], + "version": "0.5.0", + "authors": [ + { + "name": "Alex Runyan", + "email": "alex@runyan.co" + } + ], + "require": { + "php": ">=7.2", + "ext-json": "*", + "ext-posix": "*", + "illuminate/support": "^8.73", + "illuminate/container": "^8.73", + "mnapoli/silly": "^1.7", + "react/child-process": "^0.6.4", + "symfony/process": "^5.3", + "symfony/console": "^5.3", + "mockery/mockery": "^1.4", + "phpunit/phpunit": "^9.5", + "vlucas/phpdotenv": "^5.4", + "brianium/paratest": "^6.4" + }, + "require-dev": { + "beyondcode/laravel-dump-server": "^1.7" + }, + "autoload": { + "files": [ + "includes/facades.php", + "includes/helpers.php" + ], + "psr-4": { + "ProcessMaker\\Cli\\": "cli/ProcessMaker/" + } + } +} diff --git a/pm4-tools/composer.lock b/pm4-tools/composer.lock new file mode 100644 index 0000000..c26eb77 --- /dev/null +++ b/pm4-tools/composer.lock @@ -0,0 +1,5762 @@ +{ + "_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": "ca1dfce4af07399a8852edf970504a2e", + "packages": [ + { + "name": "brianium/paratest", + "version": "v6.4.1", + "source": { + "type": "git", + "url": "https://github.com/paratestphp/paratest.git", + "reference": "c32a5c4fc2ff339202437d25d19a5f496f880d61" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/c32a5c4fc2ff339202437d25d19a5f496f880d61", + "reference": "c32a5c4fc2ff339202437d25d19a5f496f880d61", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-simplexml": "*", + "php": "^7.3 || ^8.0", + "phpunit/php-code-coverage": "^9.2.9", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-timer": "^5.0.3", + "phpunit/phpunit": "^9.5.10", + "sebastian/environment": "^5.1.3", + "symfony/console": "^5.4.0 || ^6.0.0", + "symfony/process": "^5.4.0 || ^6.0.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9.0.0", + "ekino/phpstan-banned-code": "^0.5.0", + "ergebnis/phpstan-rules": "^0.15.3", + "ext-posix": "*", + "infection/infection": "^0.25.3", + "malukenho/mcbumpface": "^1.1.5", + "phpstan/phpstan": "^0.12.99", + "phpstan/phpstan-deprecation-rules": "^0.12.6", + "phpstan/phpstan-phpunit": "^0.12.22", + "phpstan/phpstan-strict-rules": "^0.12.11", + "squizlabs/php_codesniffer": "^3.6.1", + "symfony/filesystem": "^v5.4.0", + "thecodingmachine/phpstan-strict-rules": "^v0.12.2", + "vimeo/psalm": "^4.13.1" + }, + "bin": [ + "bin/paratest" + ], + "type": "library", + "autoload": { + "psr-4": { + "ParaTest\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Scaturro", + "email": "scaturrob@gmail.com", + "role": "Developer" + }, + { + "name": "Filippo Tessarotto", + "email": "zoeslam@gmail.com", + "role": "Developer" + } + ], + "description": "Parallel testing for PHP", + "homepage": "https://github.com/paratestphp/paratest", + "keywords": [ + "concurrent", + "parallel", + "phpunit", + "testing" + ], + "support": { + "issues": "https://github.com/paratestphp/paratest/issues", + "source": "https://github.com/paratestphp/paratest/tree/v6.4.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/Slamdunk", + "type": "github" + }, + { + "url": "https://paypal.me/filippotessarotto", + "type": "paypal" + } + ], + "time": "2021-12-02T09:12:23+00:00" + }, + { + "name": "doctrine/inflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89", + "reference": "8b7ff3e4b7de6b2c84da85637b59fd2880ecaa89", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^8.2", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", + "phpstan/phpstan-strict-rules": "^0.12", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "vimeo/psalm": "^4.10" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", + "keywords": [ + "inflection", + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" + } + ], + "time": "2021-10-22T20:16:43+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b", + "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^8.0", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^0.13 || 1.0.0-alpha2", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/1.4.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2020-11-10T18:47:58+00:00" + }, + { + "name": "evenement/evenement", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/igorw/evenement.git", + "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/igorw/evenement/zipball/531bfb9d15f8aa57454f5f0285b18bec903b8fb7", + "reference": "531bfb9d15f8aa57454f5f0285b18bec903b8fb7", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "Evenement": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + } + ], + "description": "Événement is a very simple event dispatching library for PHP", + "keywords": [ + "event-dispatcher", + "event-emitter" + ], + "support": { + "issues": "https://github.com/igorw/evenement/issues", + "source": "https://github.com/igorw/evenement/tree/master" + }, + "time": "2017-07-23T21:35:13+00:00" + }, + { + "name": "graham-campbell/result-type", + "version": "v1.0.4", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "0690bde05318336c7221785f2a932467f98b64ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/0690bde05318336c7221785f2a932467f98b64ca", + "reference": "0690bde05318336c7221785f2a932467f98b64ca", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "phpoption/phpoption": "^1.8" + }, + "require-dev": { + "phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.19 || ^9.5.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\ResultType\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "An Implementation Of The Result Type", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Result Type", + "Result-Type", + "result" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.0.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2021-11-21T21:41:47+00:00" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "shasum": "" + }, + "require": { + "php": "^5.3|^7.0|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" + }, + "time": "2020-07-09T08:09:16+00:00" + }, + { + "name": "illuminate/collections", + "version": "v8.75.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/collections.git", + "reference": "5a018387352afa2af30fd2be0a78c31e93295720" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/collections/zipball/5a018387352afa2af30fd2be0a78c31e93295720", + "reference": "5a018387352afa2af30fd2be0a78c31e93295720", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^8.0", + "illuminate/macroable": "^8.0", + "php": "^7.3|^8.0" + }, + "suggest": { + "symfony/var-dumper": "Required to use the dump method (^5.4)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + }, + "files": [ + "helpers.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Collections package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2021-12-07T14:48:29+00:00" + }, + { + "name": "illuminate/container", + "version": "v8.75.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/container.git", + "reference": "6ac391bb27391706c5f921b85060aa2c4ca03fae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/container/zipball/6ac391bb27391706c5f921b85060aa2c4ca03fae", + "reference": "6ac391bb27391706c5f921b85060aa2c4ca03fae", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^8.0", + "php": "^7.3|^8.0", + "psr/container": "^1.0" + }, + "provide": { + "psr/container-implementation": "1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Container\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Container package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2021-11-17T15:04:30+00:00" + }, + { + "name": "illuminate/contracts", + "version": "v8.75.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/contracts.git", + "reference": "b07755f7c456cf587dfbfd6f0854f9f7c1a34b2f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/b07755f7c456cf587dfbfd6f0854f9f7c1a34b2f", + "reference": "b07755f7c456cf587dfbfd6f0854f9f7c1a34b2f", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0", + "psr/container": "^1.0", + "psr/simple-cache": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Contracts\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Contracts package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2021-12-07T08:18:44+00:00" + }, + { + "name": "illuminate/macroable", + "version": "v8.75.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/macroable.git", + "reference": "aed81891a6e046fdee72edd497f822190f61c162" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/macroable/zipball/aed81891a6e046fdee72edd497f822190f61c162", + "reference": "aed81891a6e046fdee72edd497f822190f61c162", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Macroable package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2021-11-16T13:57:03+00:00" + }, + { + "name": "illuminate/support", + "version": "v8.75.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/support.git", + "reference": "f542bb78a8a1013ab33fbe14974c45b2a88993d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/support/zipball/f542bb78a8a1013ab33fbe14974c45b2a88993d0", + "reference": "f542bb78a8a1013ab33fbe14974c45b2a88993d0", + "shasum": "" + }, + "require": { + "doctrine/inflector": "^1.4|^2.0", + "ext-json": "*", + "ext-mbstring": "*", + "illuminate/collections": "^8.0", + "illuminate/contracts": "^8.0", + "illuminate/macroable": "^8.0", + "nesbot/carbon": "^2.53.1", + "php": "^7.3|^8.0", + "voku/portable-ascii": "^1.4.8" + }, + "conflict": { + "tightenco/collect": "<5.5.33" + }, + "suggest": { + "illuminate/filesystem": "Required to use the composer class (^8.0).", + "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^1.3|^2.0.2).", + "ramsey/uuid": "Required to use Str::uuid() (^4.2.2).", + "symfony/process": "Required to use the composer class (^5.4).", + "symfony/var-dumper": "Required to use the dd function (^5.4).", + "vlucas/phpdotenv": "Required to use the Env class and env helper (^5.2)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + }, + "files": [ + "helpers.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Support package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2021-12-03T15:33:45+00:00" + }, + { + "name": "mnapoli/silly", + "version": "1.7.2", + "source": { + "type": "git", + "url": "https://github.com/mnapoli/silly.git", + "reference": "66807f87abd2ab8e5708754d70b4b601f5614c32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mnapoli/silly/zipball/66807f87abd2ab8e5708754d70b4b601f5614c32", + "reference": "66807f87abd2ab8e5708754d70b4b601f5614c32", + "shasum": "" + }, + "require": { + "php": ">=7.0", + "php-di/invoker": "~2.0", + "psr/container": "^1.0", + "symfony/console": "~3.0|~4.0|~5.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.12", + "mnapoli/phpunit-easymock": "~1.0", + "phpunit/phpunit": "~6.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Silly\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Silly CLI micro-framework based on Symfony Console", + "keywords": [ + "PSR-11", + "cli", + "console", + "framework", + "micro-framework", + "silly" + ], + "support": { + "issues": "https://github.com/mnapoli/silly/issues", + "source": "https://github.com/mnapoli/silly/tree/1.7.2" + }, + "time": "2019-11-26T20:07:27+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.4.4", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "e01123a0e847d52d186c5eb4b9bf58b0c6d00346" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/e01123a0e847d52d186c5eb4b9bf58b0c6d00346", + "reference": "e01123a0e847d52d186c5eb4b9bf58b0c6d00346", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": "^7.3 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "psr-0": { + "Mockery": "library/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "http://blog.astrumfutura.com" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "http://davedevelopment.co.uk" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "issues": "https://github.com/mockery/mockery/issues", + "source": "https://github.com/mockery/mockery/tree/1.4.4" + }, + "time": "2021-09-13T15:28:59+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.10.2", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220", + "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "replace": { + "myclabs/deep-copy": "self.version" + }, + "require-dev": { + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^7.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + }, + "files": [ + "src/DeepCopy/deep_copy.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2020-11-13T09:40:50+00:00" + }, + { + "name": "nesbot/carbon", + "version": "2.55.2", + "source": { + "type": "git", + "url": "https://github.com/briannesbitt/Carbon.git", + "reference": "8c2a18ce3e67c34efc1b29f64fe61304368259a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/8c2a18ce3e67c34efc1b29f64fe61304368259a2", + "reference": "8c2a18ce3e67c34efc1b29f64fe61304368259a2", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^7.1.8 || ^8.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php80": "^1.16", + "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" + }, + "require-dev": { + "doctrine/dbal": "^2.0 || ^3.0", + "doctrine/orm": "^2.7", + "friendsofphp/php-cs-fixer": "^3.0", + "kylekatarnls/multi-tester": "^2.0", + "phpmd/phpmd": "^2.9", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.54", + "phpunit/phpunit": "^7.5.20 || ^8.5.14", + "squizlabs/php_codesniffer": "^3.4" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-3.x": "3.x-dev", + "dev-master": "2.x-dev" + }, + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "Carbon\\": "src/Carbon/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "https://markido.com" + }, + { + "name": "kylekatarnls", + "homepage": "https://github.com/kylekatarnls" + } + ], + "description": "An API extension for DateTime that supports 281 different languages.", + "homepage": "https://carbon.nesbot.com", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "docs": "https://carbon.nesbot.com/docs", + "issues": "https://github.com/briannesbitt/Carbon/issues", + "source": "https://github.com/briannesbitt/Carbon" + }, + "funding": [ + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2021-12-03T14:59:52+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.13.2", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "210577fe3cf7badcc5814d99455df46564f3c077" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/210577fe3cf7badcc5814d99455df46564f3c077", + "reference": "210577fe3cf7badcc5814d99455df46564f3c077", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.2" + }, + "time": "2021-11-30T19:35:32+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" + }, + { + "name": "phar-io/version", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "bae7c545bef187884426f042434e561ab1ddb182" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/bae7c545bef187884426f042434e561ab1ddb182", + "reference": "bae7c545bef187884426f042434e561ab1ddb182", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.1.0" + }, + "time": "2021-02-23T14:00:09+00:00" + }, + { + "name": "php-di/invoker", + "version": "2.3.2", + "source": { + "type": "git", + "url": "https://github.com/PHP-DI/Invoker.git", + "reference": "5214cbe5aad066022cd845dbf313f0e47aed928f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/5214cbe5aad066022cd845dbf313f0e47aed928f", + "reference": "5214cbe5aad066022cd845dbf313f0e47aed928f", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "psr/container": "^1.0|^2.0" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "mnapoli/hard-mode": "~0.3.0", + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Invoker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Generic and extensible callable invoker", + "homepage": "https://github.com/PHP-DI/Invoker", + "keywords": [ + "callable", + "dependency", + "dependency-injection", + "injection", + "invoke", + "invoker" + ], + "support": { + "issues": "https://github.com/PHP-DI/Invoker/issues", + "source": "https://github.com/PHP-DI/Invoker/tree/2.3.2" + }, + "funding": [ + { + "url": "https://github.com/mnapoli", + "type": "github" + } + ], + "time": "2021-07-30T15:05:32+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, + "time": "2021-10-19T17:43:47+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.5.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/a12f7e301eb7258bb68acd89d4aefa05c2906cae", + "reference": "a12f7e301eb7258bb68acd89d4aefa05c2906cae", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.1" + }, + "time": "2021-10-02T14:08:47+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.8.1", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15", + "reference": "eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.19 || ^9.5.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.8.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2021-12-04T23:24:31+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "1.14.0", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", + "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.2", + "php": "^7.2 || ~8.0, <8.2", + "phpdocumentor/reflection-docblock": "^5.2", + "sebastian/comparator": "^3.0 || ^4.0", + "sebastian/recursion-context": "^3.0 || ^4.0" + }, + "require-dev": { + "phpspec/phpspec": "^6.0 || ^7.0", + "phpunit/phpunit": "^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Prophecy\\": "src/Prophecy" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/1.14.0" + }, + "time": "2021-09-10T09:02:12+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.10", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "d5850aaf931743067f4bfc1ae4cbd06468400687" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d5850aaf931743067f4bfc1ae4cbd06468400687", + "reference": "d5850aaf931743067f4bfc1ae4cbd06468400687", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.13.0", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "*", + "ext-xdebug": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.10" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-05T09:12:13+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.5.10", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c814a05837f2edb0d1471d6e3f4ab3501ca3899a", + "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpspec/prophecy": "^1.12.1", + "phpunit/php-code-coverage": "^9.2.7", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.5", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.3", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^2.3.4", + "sebastian/version": "^3.0.2" + }, + "require-dev": { + "ext-pdo": "*", + "phpspec/prophecy-phpunit": "^2.0.1" + }, + "suggest": { + "ext-soap": "*", + "ext-xdebug": "*" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.5-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ], + "files": [ + "src/Framework/Assert/Functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.10" + }, + "funding": [ + { + "url": "https://phpunit.de/donate.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-09-25T07:38:51+00:00" + }, + { + "name": "psr/container", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", + "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/1.1.2" + }, + "time": "2021-11-05T16:50:12+00:00" + }, + { + "name": "psr/simple-cache", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/master" + }, + "time": "2017-10-23T01:57:42+00:00" + }, + { + "name": "react/child-process", + "version": "v0.6.4", + "source": { + "type": "git", + "url": "https://github.com/reactphp/child-process.git", + "reference": "a778f3fb828d68caf8a9ab6567fd8342a86f12fe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/child-process/zipball/a778f3fb828d68caf8a9ab6567fd8342a86f12fe", + "reference": "a778f3fb828d68caf8a9ab6567fd8342a86f12fe", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/event-loop": "^1.2", + "react/stream": "^1.2" + }, + "require-dev": { + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35", + "react/socket": "^1.8", + "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\ChildProcess\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Event-driven library for executing child processes with ReactPHP.", + "keywords": [ + "event-driven", + "process", + "reactphp" + ], + "support": { + "issues": "https://github.com/reactphp/child-process/issues", + "source": "https://github.com/reactphp/child-process/tree/v0.6.4" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2021-10-12T10:37:07+00:00" + }, + { + "name": "react/event-loop", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/event-loop.git", + "reference": "be6dee480fc4692cec0504e65eb486e3be1aa6f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/be6dee480fc4692cec0504e65eb486e3be1aa6f2", + "reference": "be6dee480fc4692cec0504e65eb486e3be1aa6f2", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" + }, + "suggest": { + "ext-event": "~1.0 for ExtEventLoop", + "ext-pcntl": "For signal handling support when using the StreamSelectLoop", + "ext-uv": "* for ExtUvLoop" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\EventLoop\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", + "keywords": [ + "asynchronous", + "event-loop" + ], + "support": { + "issues": "https://github.com/reactphp/event-loop/issues", + "source": "https://github.com/reactphp/event-loop/tree/v1.2.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2021-07-11T12:31:24+00:00" + }, + { + "name": "react/stream", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/stream.git", + "reference": "7a423506ee1903e89f1e08ec5f0ed430ff784ae9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/stream/zipball/7a423506ee1903e89f1e08ec5f0ed430ff784ae9", + "reference": "7a423506ee1903e89f1e08ec5f0ed430ff784ae9", + "shasum": "" + }, + "require": { + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.8", + "react/event-loop": "^1.2" + }, + "require-dev": { + "clue/stream-filter": "~1.2", + "phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Stream\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", + "keywords": [ + "event-driven", + "io", + "non-blocking", + "pipe", + "reactphp", + "readable", + "stream", + "writable" + ], + "support": { + "issues": "https://github.com/reactphp/stream/issues", + "source": "https://github.com/reactphp/stream/tree/v1.2.0" + }, + "funding": [ + { + "url": "https://github.com/WyriHaximus", + "type": "github" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2021-07-11T12:37:55+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "55f4261989e546dc112258c7a75935a81a7ce382" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55f4261989e546dc112258c7a75935a81a7ce382", + "reference": "55f4261989e546dc112258c7a75935a81a7ce382", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:49:45+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.7", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:52:27+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:10:38+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "388b6ced16caa751030f6a69e588299fa09200ac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac", + "reference": "388b6ced16caa751030f6a69e588299fa09200ac", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:52:38+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9", + "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-11-11T14:18:36+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/23bd5951f7ff26f12d4e3242864df3e08dec4e49", + "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-06-11T13:31:12+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-28T06:42:11+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/cd9d8cf3c5804de4341c283ed787f099f5506172", + "reference": "cd9d8cf3c5804de4341c283ed787f099f5506172", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:17:30+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:45:17+00:00" + }, + { + "name": "sebastian/type", + "version": "2.3.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b8cd8a1c753c90bc1a0f5372170e3e489136f914", + "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/2.3.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-06-15T12:49:02+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "symfony/console", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "ec3661faca1d110d6c307e124b44f99ac54179e3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/ec3661faca1d110d6c307e124b44f99ac54179e3", + "reference": "ec3661faca1d110d6c307e124b44f99ac54179e3", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.8", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/string": "^5.1|^6.0" + }, + "conflict": { + "psr/log": ">=3", + "symfony/dependency-injection": "<4.4", + "symfony/dotenv": "<5.1", + "symfony/event-dispatcher": "<4.4", + "symfony/lock": "<4.4", + "symfony/process": "<4.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0" + }, + "require-dev": { + "psr/log": "^1|^2", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/event-dispatcher": "^4.4|^5.0|^6.0", + "symfony/lock": "^4.4|^5.0|^6.0", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/var-dumper": "^4.4|^5.0|^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/v5.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": "2021-11-29T15:30:56+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v2.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/6f981ee24cf69ee7ce9736146d1c57c2780598a8", + "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-07-12T14:48:14+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.23.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce", + "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "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.23.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": "2021-02-19T12:13:01+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.23.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "16880ba9c5ebe3642d1995ab866db29270b36535" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/16880ba9c5ebe3642d1995ab866db29270b36535", + "reference": "16880ba9c5ebe3642d1995ab866db29270b36535", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + }, + "files": [ + "bootstrap.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": "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.23.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": "2021-05-27T12:26:48+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.23.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8", + "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "files": [ + "bootstrap.php" + ], + "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.23.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": "2021-02-19T12:13:01+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.23.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9174a3d80210dca8daa7f31fec659150bbeabfc6", + "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.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": "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.23.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": "2021-05-27T12:26:48+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.23.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fba8933c384d6476ab14fb7b8526e5287ca7e010", + "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.23.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": "2021-02-19T12:13:01+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.23.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/1100343ed1a92e3a38f9ae122fc0eb21602547be", + "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.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": "2021-07-28T13:41:28+00:00" + }, + { + "name": "symfony/process", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "5be20b3830f726e019162b26223110c8f47cf274" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/5be20b3830f726e019162b26223110c8f47cf274", + "reference": "5be20b3830f726e019162b26223110c8f47cf274", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v5.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": "2021-11-28T15:25:38+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v2.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc", + "reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/container": "^1.1", + "symfony/deprecation-contracts": "^2.1" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v2.5.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": "2021-11-04T16:48:04+00:00" + }, + { + "name": "symfony/string", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "9ffaaba53c61ba75a3c7a3a779051d1e9ec4fd2d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/9ffaaba53c61ba75a3c7a3a779051d1e9ec4fd2d", + "reference": "9ffaaba53c61ba75a3c7a3a779051d1e9ec4fd2d", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "~1.15" + }, + "conflict": { + "symfony/translation-contracts": ">=3.0" + }, + "require-dev": { + "symfony/error-handler": "^4.4|^5.0|^6.0", + "symfony/http-client": "^4.4|^5.0|^6.0", + "symfony/translation-contracts": "^1.1|^2", + "symfony/var-exporter": "^4.4|^5.0|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "files": [ + "Resources/functions.php" + ], + "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/v5.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": "2021-11-24T10:02:00+00:00" + }, + { + "name": "symfony/translation", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "6fe32b10e912a518805bc9eafc2a87145773cf13" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/6fe32b10e912a518805bc9eafc2a87145773cf13", + "reference": "6fe32b10e912a518805bc9eafc2a87145773cf13", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "^1.16", + "symfony/translation-contracts": "^2.3" + }, + "conflict": { + "symfony/config": "<4.4", + "symfony/console": "<5.3", + "symfony/dependency-injection": "<5.0", + "symfony/http-kernel": "<5.0", + "symfony/twig-bundle": "<5.0", + "symfony/yaml": "<4.4" + }, + "provide": { + "symfony/translation-implementation": "2.3" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/dependency-injection": "^5.0|^6.0", + "symfony/finder": "^4.4|^5.0|^6.0", + "symfony/http-client-contracts": "^1.1|^2.0|^3.0", + "symfony/http-kernel": "^5.0|^6.0", + "symfony/intl": "^4.4|^5.0|^6.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/service-contracts": "^1.1.2|^2|^3", + "symfony/yaml": "^4.4|^5.0|^6.0" + }, + "suggest": { + "psr/log-implementation": "To use logging capability in translator", + "symfony/config": "", + "symfony/yaml": "" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "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 tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v5.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": "2021-11-29T15:30:56+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v2.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "d28150f0f44ce854e942b671fc2620a98aae1b1e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/d28150f0f44ce854e942b671fc2620a98aae1b1e", + "reference": "d28150f0f44ce854e942b671fc2620a98aae1b1e", + "shasum": "" + }, + "require": { + "php": ">=7.2.5" + }, + "suggest": { + "symfony/translation-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + } + }, + "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 translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v2.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-08-17T14:20:01+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "d4394d044ed69a8f244f3445bcedf8a0d7fe2403" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/d4394d044ed69a8f244f3445bcedf8a0d7fe2403", + "reference": "d4394d044ed69a8f244f3445bcedf8a0d7fe2403", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.0.2", + "php": "^7.1.3 || ^8.0", + "phpoption/phpoption": "^1.8", + "symfony/polyfill-ctype": "^1.23", + "symfony/polyfill-mbstring": "^1.23.1", + "symfony/polyfill-php80": "^1.23.1" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "ext-filter": "*", + "phpunit/phpunit": "^7.5.20 || ^8.5.21 || ^9.5.10" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.4-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk" + }, + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com" + } + ], + "description": "Loads environment variables from `.env` to `Config::env()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.4.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2021-11-10T01:08:39+00:00" + }, + { + "name": "voku/portable-ascii", + "version": "1.5.6", + "source": { + "type": "git", + "url": "https://github.com/voku/portable-ascii.git", + "reference": "80953678b19901e5165c56752d087fc11526017c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/80953678b19901e5165c56752d087fc11526017c", + "reference": "80953678b19901e5165c56752d087fc11526017c", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + }, + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" + }, + "type": "library", + "autoload": { + "psr-4": { + "voku\\": "src/voku/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lars Moelleken", + "homepage": "http://www.moelleken.org/" + } + ], + "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", + "homepage": "https://github.com/voku/portable-ascii", + "keywords": [ + "ascii", + "clean", + "php" + ], + "support": { + "issues": "https://github.com/voku/portable-ascii/issues", + "source": "https://github.com/voku/portable-ascii/tree/1.5.6" + }, + "funding": [ + { + "url": "https://www.paypal.me/moelleken", + "type": "custom" + }, + { + "url": "https://github.com/voku", + "type": "github" + }, + { + "url": "https://opencollective.com/portable-ascii", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", + "type": "tidelift" + } + ], + "time": "2020-11-12T00:07:28+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.10.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25", + "reference": "6964c76c7804814a842473e0c8fd15bab0f18e25", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.10.0" + }, + "time": "2021-03-09T10:59:23+00:00" + } + ], + "packages-dev": [ + { + "name": "beyondcode/laravel-dump-server", + "version": "1.7.0", + "source": { + "type": "git", + "url": "https://github.com/beyondcode/laravel-dump-server.git", + "reference": "e27c7b942ab62f6ac7168359393d328ec5215b89" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/beyondcode/laravel-dump-server/zipball/e27c7b942ab62f6ac7168359393d328ec5215b89", + "reference": "e27c7b942ab62f6ac7168359393d328ec5215b89", + "shasum": "" + }, + "require": { + "illuminate/console": "5.6.*|5.7.*|5.8.*|^6.0|^7.0|^8.0", + "illuminate/http": "5.6.*|5.7.*|5.8.*|^6.0|^7.0|^8.0", + "illuminate/support": "5.6.*|5.7.*|5.8.*|^6.0|^7.0|^8.0", + "php": ">=7.2.5", + "symfony/var-dumper": "^5.0" + }, + "require-dev": { + "larapack/dd": "^1.0", + "phpunit/phpunit": "^7.0|^9.3" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "BeyondCode\\DumpServer\\DumpServerServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "BeyondCode\\DumpServer\\": "src" + }, + "files": [ + "helpers.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marcel Pociot", + "email": "marcel@beyondco.de", + "homepage": "https://beyondco.de", + "role": "Developer" + } + ], + "description": "Symfony Var-Dump Server for Laravel", + "homepage": "https://github.com/beyondcode/laravel-dump-server", + "keywords": [ + "beyondcode", + "laravel-dump-server" + ], + "support": { + "issues": "https://github.com/beyondcode/laravel-dump-server/issues", + "source": "https://github.com/beyondcode/laravel-dump-server/tree/1.7.0" + }, + "time": "2020-12-15T10:57:43+00:00" + }, + { + "name": "illuminate/console", + "version": "v8.75.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/console.git", + "reference": "1f952710c671b8ca90bb10be585ebcc94f72166a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/console/zipball/1f952710c671b8ca90bb10be585ebcc94f72166a", + "reference": "1f952710c671b8ca90bb10be585ebcc94f72166a", + "shasum": "" + }, + "require": { + "illuminate/collections": "^8.0", + "illuminate/contracts": "^8.0", + "illuminate/macroable": "^8.0", + "illuminate/support": "^8.0", + "php": "^7.3|^8.0", + "symfony/console": "^5.4", + "symfony/process": "^5.4" + }, + "suggest": { + "dragonmantank/cron-expression": "Required to use scheduler (^3.0.2).", + "guzzlehttp/guzzle": "Required to use the ping methods on schedules (^6.5.5|^7.0.1).", + "illuminate/bus": "Required to use the scheduled job dispatcher (^8.0).", + "illuminate/container": "Required to use the scheduler (^8.0).", + "illuminate/filesystem": "Required to use the generator command (^8.0).", + "illuminate/queue": "Required to use closures for scheduled jobs (^8.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Console\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Console package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2021-11-30T14:13:40+00:00" + }, + { + "name": "illuminate/filesystem", + "version": "v8.75.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/filesystem.git", + "reference": "b8f4b43a65d0e93312d8599bc4e86e21f68f7ccf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/filesystem/zipball/b8f4b43a65d0e93312d8599bc4e86e21f68f7ccf", + "reference": "b8f4b43a65d0e93312d8599bc4e86e21f68f7ccf", + "shasum": "" + }, + "require": { + "illuminate/collections": "^8.0", + "illuminate/contracts": "^8.0", + "illuminate/macroable": "^8.0", + "illuminate/support": "^8.0", + "php": "^7.3|^8.0", + "symfony/finder": "^5.4" + }, + "suggest": { + "ext-ftp": "Required to use the Flysystem FTP driver.", + "illuminate/http": "Required for handling uploaded files (^7.0).", + "league/flysystem": "Required to use the Flysystem local and FTP drivers (^1.1).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^1.0).", + "league/flysystem-cached-adapter": "Required to use the Flysystem cache (^1.0).", + "league/flysystem-sftp": "Required to use the Flysystem SFTP driver (^1.0).", + "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^5.4).", + "symfony/mime": "Required to enable support for guessing extensions (^5.4)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Filesystem\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Filesystem package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2021-11-30T14:13:40+00:00" + }, + { + "name": "illuminate/http", + "version": "v8.75.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/http.git", + "reference": "9750b34b7df8f5a3ef66328dc10a7a51e5d13f6c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/http/zipball/9750b34b7df8f5a3ef66328dc10a7a51e5d13f6c", + "reference": "9750b34b7df8f5a3ef66328dc10a7a51e5d13f6c", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/collections": "^8.0", + "illuminate/macroable": "^8.0", + "illuminate/session": "^8.0", + "illuminate/support": "^8.0", + "php": "^7.3|^8.0", + "symfony/http-foundation": "^5.4", + "symfony/http-kernel": "^5.4", + "symfony/mime": "^5.4" + }, + "suggest": { + "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().", + "guzzlehttp/guzzle": "Required to use the HTTP Client (^6.5.5|^7.0.1)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Http\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Http package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2021-11-30T21:48:40+00:00" + }, + { + "name": "illuminate/session", + "version": "v8.75.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/session.git", + "reference": "c5964a2348354f266fb18aac60eed0b81355c258" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/session/zipball/c5964a2348354f266fb18aac60eed0b81355c258", + "reference": "c5964a2348354f266fb18aac60eed0b81355c258", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/collections": "^8.0", + "illuminate/contracts": "^8.0", + "illuminate/filesystem": "^8.0", + "illuminate/support": "^8.0", + "php": "^7.3|^8.0", + "symfony/finder": "^5.4", + "symfony/http-foundation": "^5.4" + }, + "suggest": { + "illuminate/console": "Required to use the session:table command (^8.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Session\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Session package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2021-11-30T14:13:40+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, + { + "name": "symfony/error-handler", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/error-handler.git", + "reference": "8433fa3145ac78df88b87a4a539118e950828126" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/8433fa3145ac78df88b87a4a539118e950828126", + "reference": "8433fa3145ac78df88b87a4a539118e950828126", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/log": "^1|^2|^3", + "symfony/var-dumper": "^4.4|^5.0|^6.0" + }, + "require-dev": { + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/http-kernel": "^4.4|^5.0|^6.0", + "symfony/serializer": "^4.4|^5.0|^6.0" + }, + "bin": [ + "Resources/bin/patch-type-declarations" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ErrorHandler\\": "" + }, + "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 tools to manage errors and ease debugging PHP code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/error-handler/tree/v5.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": "2021-11-29T15:30:56+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "27d39ae126352b9fa3be5e196ccf4617897be3eb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/27d39ae126352b9fa3be5e196ccf4617897be3eb", + "reference": "27d39ae126352b9fa3be5e196ccf4617897be3eb", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/event-dispatcher-contracts": "^2|^3", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "symfony/dependency-injection": "<4.4" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/error-handler": "^4.4|^5.0|^6.0", + "symfony/expression-language": "^4.4|^5.0|^6.0", + "symfony/http-foundation": "^4.4|^5.0|^6.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/stopwatch": "^4.4|^5.0|^6.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "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 tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v5.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": "2021-11-23T10:19:22+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v2.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "66bea3b09be61613cd3b4043a65a8ec48cfa6d2a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/66bea3b09be61613cd3b4043a65a8ec48cfa6d2a", + "reference": "66bea3b09be61613cd3b4043a65a8ec48cfa6d2a", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/event-dispatcher": "^1" + }, + "suggest": { + "symfony/event-dispatcher-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "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 dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-07-12T14:48:14+00:00" + }, + { + "name": "symfony/finder", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "d2f29dac98e96a98be467627bd49c2efb1bc2590" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/d2f29dac98e96a98be467627bd49c2efb1bc2590", + "reference": "d2f29dac98e96a98be467627bd49c2efb1bc2590", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "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": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v5.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": "2021-11-28T15:25:38+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "5ef86ac7927d2de08dc1e26eb91325f9ccbe6309" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/5ef86ac7927d2de08dc1e26eb91325f9ccbe6309", + "reference": "5ef86ac7927d2de08dc1e26eb91325f9ccbe6309", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "predis/predis": "~1.0", + "symfony/cache": "^4.4|^5.0|^6.0", + "symfony/expression-language": "^4.4|^5.0|^6.0", + "symfony/mime": "^4.4|^5.0|^6.0" + }, + "suggest": { + "symfony/mime": "To use the file extension guesser" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "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": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v5.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": "2021-11-28T15:25:38+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "e012f16688bcb151e965473a70d8ebaa8b1d15ea" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/e012f16688bcb151e965473a70d8ebaa8b1d15ea", + "reference": "e012f16688bcb151e965473a70d8ebaa8b1d15ea", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "psr/log": "^1|^2", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/error-handler": "^4.4|^5.0|^6.0", + "symfony/event-dispatcher": "^5.0|^6.0", + "symfony/http-foundation": "^5.3.7|^6.0", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-php73": "^1.9", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "symfony/browser-kit": "<5.4", + "symfony/cache": "<5.0", + "symfony/config": "<5.0", + "symfony/console": "<4.4", + "symfony/dependency-injection": "<5.3", + "symfony/doctrine-bridge": "<5.0", + "symfony/form": "<5.0", + "symfony/http-client": "<5.0", + "symfony/mailer": "<5.0", + "symfony/messenger": "<5.0", + "symfony/translation": "<5.0", + "symfony/twig-bridge": "<5.0", + "symfony/validator": "<5.0", + "twig/twig": "<2.13" + }, + "provide": { + "psr/log-implementation": "1.0|2.0" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/browser-kit": "^5.4|^6.0", + "symfony/config": "^5.0|^6.0", + "symfony/console": "^4.4|^5.0|^6.0", + "symfony/css-selector": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^5.3|^6.0", + "symfony/dom-crawler": "^4.4|^5.0|^6.0", + "symfony/expression-language": "^4.4|^5.0|^6.0", + "symfony/finder": "^4.4|^5.0|^6.0", + "symfony/http-client-contracts": "^1.1|^2|^3", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/routing": "^4.4|^5.0|^6.0", + "symfony/stopwatch": "^4.4|^5.0|^6.0", + "symfony/translation": "^4.4|^5.0|^6.0", + "symfony/translation-contracts": "^1.1|^2|^3", + "twig/twig": "^2.13|^3.0.4" + }, + "suggest": { + "symfony/browser-kit": "", + "symfony/config": "", + "symfony/console": "", + "symfony/dependency-injection": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "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 a structured process for converting a Request into a Response", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-kernel/tree/v5.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": "2021-11-29T16:56:53+00:00" + }, + { + "name": "symfony/mime", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "d4365000217b67c01acff407573906ff91bcfb34" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/d4365000217b67c01acff407573906ff91bcfb34", + "reference": "d4365000217b67c01acff407573906ff91bcfb34", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/mailer": "<4.4" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/property-access": "^4.4|^5.1|^6.0", + "symfony/property-info": "^4.4|^5.1|^6.0", + "symfony/serializer": "^5.2|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "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": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + "source": "https://github.com/symfony/mime/tree/v5.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": "2021-11-23T10:19:22+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.23.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "65bd267525e82759e7d8c4e8ceea44f398838e65" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/65bd267525e82759e7d8c4e8ceea44f398838e65", + "reference": "65bd267525e82759e7d8c4e8ceea44f398838e65", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "symfony/polyfill-intl-normalizer": "^1.10", + "symfony/polyfill-php72": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.23.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": "2021-05-27T09:27:20+00:00" + }, + { + "name": "symfony/polyfill-php72", + "version": "v1.23.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "9a142215a36a3888e30d0a9eeea9766764e96976" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/9a142215a36a3888e30d0a9eeea9766764e96976", + "reference": "9a142215a36a3888e30d0a9eeea9766764e96976", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php72\\": "" + }, + "files": [ + "bootstrap.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": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php72/tree/v1.23.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": "2021-05-27T09:17:38+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "89ab66eaef230c9cd1992de2e9a1b26652b127b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/89ab66eaef230c9cd1992de2e9a1b26652b127b9", + "reference": "89ab66eaef230c9cd1992de2e9a1b26652b127b9", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "phpunit/phpunit": "<5.4.3", + "symfony/console": "<4.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^4.4|^5.0|^6.0", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/uid": "^5.1|^6.0", + "twig/twig": "^2.13|^3.0.4" + }, + "suggest": { + "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", + "ext-intl": "To show region name in time zone dump", + "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v5.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": "2021-11-29T15:30:56+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=7.2", + "ext-json": "*", + "ext-posix": "*" + }, + "platform-dev": [], + "plugin-api-version": "2.0.0" +} diff --git a/pm4-tools/includes/facades.php b/pm4-tools/includes/facades.php new file mode 100644 index 0000000..f42eed7 --- /dev/null +++ b/pm4-tools/includes/facades.php @@ -0,0 +1,83 @@ +make(static::containerKey()); + + return call_user_func_array([$resolvedInstance, $method], $parameters); + } +} + +/** + * @see \ProcessMaker\Cli\CommandLine + */ +class CommandLine extends Facade {} + +/** + * @see \ProcessMaker\Cli\FileSystem + */ +class FileSystem extends Facade {} + +/** + * @see \ProcessMaker\Cli\Packages + */ +class Packages extends Facade {} + +/** + * @see \ProcessMaker\Cli\Install + */ +class Install extends Facade {} + +/** + * @see \ProcessMaker\Cli\ProcessManager + */ +class ProcessManager extends Facade {} + +/** + * @see \ProcessMaker\Cli\Composer + */ +class Composer extends Facade {} + +/** + * @see \ProcessMaker\Cli\Git + */ +class Git extends Facade {} + +/** + * @see \ProcessMaker\Cli\PackagesCi + */ +class PackagesCi extends Facade {} + +/** + * @see \ProcessMaker\Cli\Config + */ +class Config extends Facade {} + + +/** + * @see \ProcessMaker\Cli\Config + */ +class PhpUnit extends Facade {} \ No newline at end of file diff --git a/pm4-tools/includes/helpers.php b/pm4-tools/includes/helpers.php new file mode 100644 index 0000000..eb9bb88 --- /dev/null +++ b/pm4-tools/includes/helpers.php @@ -0,0 +1,132 @@ +runAsUser('printf $(brew --prefix)')); +// } + +if (!defined('PM_HOME_PATH')) { + define('PM_HOME_PATH', $_SERVER['HOME'] . '/.config/pm'); +} + +if (!defined('USER_HOME')) { + define('USER_HOME', getenv('HOME')); +} + +/** + * @param string $output + * @param int $exitCode + * + * @return string + */ +function warningThenExit(string $output, int $exitCode = 0): string { + return warning($output) . exit($exitCode); +} + +/** + * Resolve the given class from the container. + * + * @param string $class + * + * @return mixed + * @throws \Illuminate\Contracts\Container\BindingResolutionException + */ +function resolve(string $class) { + return Container::getInstance()->make($class); +} + +/** + * Swap the given class implementation in the container. + * + * @param string $class + * @param mixed $instance + * + * @return void + */ +function swap(string $class, $instance) { + Container::getInstance()->instance($class, $instance); +} + +/** + * @return mixed + */ +function user() { + if (! isset($_SERVER['SUDO_USER'])) { + return $_SERVER['USER']; + } + + return $_SERVER['SUDO_USER']; +} + +/** + * Verify that the script is currently running as "sudo". + * + * @return void + * @throws \Exception + */ +function should_be_sudo() { + if (!isset($_SERVER['SUDO_USER'])) { + throw new Exception('This command must be run with sudo.'); + } +} + +/** + * Output the given text to the console. + * + * @param string $output + * + * @return void + */ +function info(string $output) { + $secrets = [ + Config::env('GITHUB_TOKEN', true) + ]; + $output = str_replace($secrets, '****', $output); + output(''.$output.''); +} + +/** + * Output the given text to the console. + * + * @param string $output + * + * @return void + */ +function warning(string $output) { + output('' . $output . ''); +} + +/** + * Output a table to the console. + * + * @param array $headers + * @param array $rows + * @return void + */ +function table(array $headers = [], array $rows = []) { + $table = new Table(new ConsoleOutput); + $table->setHeaders($headers)->setRows($rows); + $table->render(); +} + +/** + * Output the given text to the console. + * + * @param string $output + * + * @return void + */ +function output(string $output) { + if (isset($_ENV['APP_ENV']) && $_ENV['APP_ENV'] === 'testing') { + return; + } + + (new ConsoleOutput())->writeln($output); +} diff --git a/pm4-tools/phpunit.xml b/pm4-tools/phpunit.xml new file mode 100644 index 0000000..1c9c237 --- /dev/null +++ b/pm4-tools/phpunit.xml @@ -0,0 +1,21 @@ + + + + + ./tests + + + + + + diff --git a/pm4-tools/pm b/pm4-tools/pm new file mode 100755 index 0000000..370125d --- /dev/null +++ b/pm4-tools/pm @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +SOURCE="${BASH_SOURCE[0]}" + +# If the current source is a symbolic link, we need to resolve it to an +# actual directory name. We'll use PHP to do this easier than we can +# do it in pure Bash. So, we'll call into PHP CLI here to resolve. +if [[ -L "$SOURCE" ]]; then + DIR=$(php -r "echo dirname(realpath('$SOURCE'));") +else + DIR="$( cd "$( dirname "$SOURCE" )" && pwd )" +fi + +# If we are in the global Composer "bin" directory, we need to bump our +# current directory up two, so that we will correctly proxy into the +# Valet CLI script which is written in PHP. Will use PHP to do it. +if [ ! -f "$DIR/cli/pm.php" ]; then + DIR=$(php -r "echo realpath('$DIR/../runyan-co/pm');") +fi + +if [[ "$EUID" -ne 0 && "$PM_CI" -ne "true" ]]; then + sudo USER="$USER" --preserve-env "$SOURCE" "$@" + exit +fi + +php "$DIR/cli/pm.php" "$@" diff --git a/pr_body_example.txt b/pr_body_example.txt new file mode 100644 index 0000000..061aab4 --- /dev/null +++ b/pr_body_example.txt @@ -0,0 +1,8 @@ +## Test PR + +Branches needed: + +ci:connector-send-email:updates-for-ci + +Done! + diff --git a/run-endtest-tests.sh b/run-endtest-tests.sh new file mode 100755 index 0000000..4f34aa7 --- /dev/null +++ b/run-endtest-tests.sh @@ -0,0 +1,84 @@ +#!/bin/bash + +## TODO: Add here the test suites that we want to execute. Ex. +# testSuites=(350178) +# testSuites=(245814 245815) +testSuites=(357469) + +declare -A testsStatus +declare -A testsHashes +declare -A testsRequest + +# Default Values +AppID=${ENDTEST_APP_ID:=""} +AppCode=${ENDTEST_APP_CODE:=""} +SERVER_URL=${SERVER_URL:=""} +PM_USER="admin" +PM_PASSWORD="admin" + +echo "Sending tests to EndTest platform" +timeElapsed=0 +exitCode=0 +for test in "${testSuites[@]}" +do + testsStatus[$test]="" + testsHashes[$test]="" + testsRequest[$test]="https://endtest.io/api.php?action=runWeb&appId=${AppID}&appCode=${AppCode}&suite=${test}&platform=windows&os=windows10&browser=chrome&resolution=1920x1200&geolocation=sanfrancisco&cases=all¬es=TestFromEndtest.cloud.processmaker.io&username=${PM_USER}&pwd=${PM_PASSWORD}&URL=${SERVER_URL}" + + echo "Get Hash for execution test suite id: $test" + testsHashes[$test]=$(curl -s -X GET --header "Accept: */*" "${testsRequest[$test]}") + echo "Hash: ${testsHashes[$test]}" + # TODO: if not_allowed then show error + pendingTest=true + echo "Test in progress..." + testTimeElapsed=0 + while $pendingTest; do + sleep 60 + timeElapsed=$((timeElapsed + 1)) + testTimeElapsed=$((testTimeElapsed + 1)) + result=$(curl -s -X GET --header "Accept: */*" "https://endtest.io/api.php?action=getResults&appId=${AppID}&appCode=${AppCode}&hash=${testsHashes[$test]}&format=json") + if [ "$result" == "Test is still running." ] + then + testsStatus[$test]=$result + echo "Status: $result Time Elapsed of this test: $testTimeElapsed minutes." + pendingTest=true + elif [ "$result" == "Processing video recording." ] + then + testsStatus[$test]=$result + echo "Status: $result Time Elapsed of this test: $testTimeElapsed minutes." + pendingTest=true + elif [ "$result" == "Stopping." ] + then + testsStatus[$test]=$result + echo "Status: $result Time Elapsed of this test: $testTimeElapsed minutes." + pendingTest=true + elif [ "$result" == "Erred." ] + then + testsStatus[$test]=$result + echo "TestSuite failed for ID: $test, Status: $result" + echo "Time Elapsed of this test: $testTimeElapsed minutes." + echo "Please check this link for detailed info: https://endtest.io/results?hash=${testsHashes[$test]}" + pendingTest=false + exitCode=1 + elif [ "$result" == "" ] + then + testsStatus[$test]=$result + echo "Status: $result Time Elapsed of this test: $testTimeElapsed minutes." + else + # TODO - Nice to have: Upload results to CI + echo "$result" > "$test".json + pendingTest=false + testsStatus[$test]="Completed" + echo "Test completed for ID: $test. Time Elapsed of this test: $testTimeElapsed minutes." + fi + done +done + +if [ $exitCode -ne 0 ]; then + echo "TestSuites finished with Errors, please see logs above." +else + echo "TestSuites finished successfully." +fi + +echo "Elapsed time of all tests: $timeElapsed" +exit $exitCode diff --git a/set_context_example.sh b/set_context_example.sh new file mode 100644 index 0000000..8fa5a7a --- /dev/null +++ b/set_context_example.sh @@ -0,0 +1,7 @@ +# Sets variables normally set by circleci but for local testing +# Edit pr_body_example.txt to set the PR's body +# run with source ./set_context_example.sh +export CI_PR_BODY="$(cat pr_body_example.txt)" +export CI_PACKAGE_BRANCH="updates-for-ci" +export CI_PROJECT="processmaker" +export IMAGE_TAG="processmaker-updates-for-ci"