Merge pull request #3401 from alchemy-fr/PHRAS-2981-builder-for-dev

PHRAS-2981 #comment merge  add dev utils to builder image
This commit is contained in:
Nicolas Maillat
2020-03-16 22:31:58 +01:00
committed by GitHub
21 changed files with 397 additions and 28 deletions

View File

@@ -23,7 +23,7 @@
/datas /datas
/docker-compose.* /docker-compose.*
/logs /logs
/nodes_modules /node_modules
/plugins /plugins
/tmp /tmp
/vendor /vendor

8
.env
View File

@@ -1,3 +1,4 @@
PHRASEANET_PROJECT_NAME=Phraseanet
# Registry from where you pull Docker images # Registry from where you pull Docker images
PHRASEANET_DOCKER_REGISTRY=local PHRASEANET_DOCKER_REGISTRY=local
# Tag of the Docker images # Tag of the Docker images
@@ -70,3 +71,10 @@ PHRASEANET_DB_DIR=./volumes/db
PHRASEANET_ELASTICSEARCH_DIR=./volumes/elasticsearch PHRASEANET_ELASTICSEARCH_DIR=./volumes/elasticsearch
PHRASEANET_THUMBNAILS_DIR=./www/thumbnails PHRASEANET_THUMBNAILS_DIR=./www/thumbnails
PHRASEANET_TMP_DIR=./tmp PHRASEANET_TMP_DIR=./tmp
# For dev who don't have SSH_AUTH_SOCK (avoid an empty volume name)
SSH_AUTH_SOCK=/dev/null
# Plugin support
PHRASEANET_PLUGINS=
PHRASEANET_SSH_PRIVATE_KEY=

View File

@@ -82,8 +82,19 @@ RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - \
&& apt-get install -y --no-install-recommends \ && apt-get install -y --no-install-recommends \
nodejs \ nodejs \
yarn \ yarn \
nano \
vim \
iputils-ping \
zsh \
ssh \
telnet \
autoconf \
libtool \
python \
pkg-config \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists \ && rm -rf /var/lib/apt/lists \
&& git clone https://github.com/robbyrussell/oh-my-zsh.git /bootstrap/.oh-my-zsh \
&& mkdir -p /var/alchemy/Phraseanet \ && mkdir -p /var/alchemy/Phraseanet \
&& chown -R app:app /var/alchemy && chown -R app:app /var/alchemy
@@ -99,10 +110,28 @@ RUN composer install --prefer-dist --no-dev --no-progress --no-suggest --classma
COPY --chown=app . . COPY --chown=app . .
RUN rm -rf docker/phraseanet/root \ RUN make install
&& make install
ADD docker/phraseanet/ / ADD ./docker/builder/root /
# SSH Private repo
ARG SSH_PRIVATE_KEY
ARG PHRASEANET_PLUGINS
RUN ( \
test ! -z "${SSH_PRIVATE_KEY}" \
&& mkdir -p ~/.ssh \
&& echo "${SSH_PRIVATE_KEY}" > ~/.ssh/id_rsa \
# make sure github domain.com is accepted
&& ssh-keyscan -H github.com >> ~/.ssh/known_hosts \
&& chmod 600 ~/.ssh/id_rsa \
) || echo "Skip SSH key"
RUN ./docker/phraseanet/plugins/console install
ENTRYPOINT ["/bootstrap/entrypoint.sh"]
CMD []
######################################################################### #########################################################################
# Phraseanet web application image # Phraseanet web application image

View File

@@ -56,10 +56,17 @@ export PHRASEANET_APP_PORT=8082
It may be easier to deal with a local file to manage our env variables. It may be easier to deal with a local file to manage our env variables.
You can add your `env.local` at the root of this project and define a command alias in your `~/.bashrc`: You can add your `env.local` at the root of this project and define a command function in your `~/.bashrc`:
```bash ```bash
alias dc="env $(cat env.local | grep -v '#' | tr '\n' ' ') docker-compose" # ~/.bashrc or ~/.zshrc
function dc() {
if [ -f env.local ]; then
env $(cat env.local | grep -v '#' | tr '\n' ' ') docker-compose $@
else
docker-compose $@
fi
}
``` ```
### Running the application ### Running the application
@@ -105,6 +112,20 @@ This can be made easily from the builder container:
> Please note that the phraseanet image does not contain nor `composer` neither `node` tools. This allow the final image to be slim. > Please note that the phraseanet image does not contain nor `composer` neither `node` tools. This allow the final image to be slim.
> If you need to use dev tools, ensure you are running the `builder` image! > If you need to use dev tools, ensure you are running the `builder` image!
### Developer shell
You can also obtain a shell access in builder container:
```bash
docker-compose run --rm builder /bin/bash
# or
docker-compose run --rm builder /bin/zsh
```
In this container you will have the same libraries (PHP, Node, composer, ...) that are used to build images.
Also you have utils for development like telnet, ping, ssh, git, ...
Your $HOME/.ssh directory is also mounted to builder's home with your ssh agent.
### Using Xdebug ### Using Xdebug
Xdebug is enabled by default with the `docker-compose.override.yml` Xdebug is enabled by default with the `docker-compose.override.yml`
@@ -132,6 +153,28 @@ XDEBUG_REMOTE_HOST=host.docker.internal
> Don't forget to recreate your container (`docker-compose up -d phraseanet`) > Don't forget to recreate your container (`docker-compose up -d phraseanet`)
### Build images with plugins
Plugins can be installed during build if you set the `PHRASEANET_PLUGINS` env var as follows:
```bash
PHRASEANET_PLUGINS="git@github.com:alchemy-fr/Phraseanet-plugin-webgallery.git"
# You can optionally precise the branch to install
# If not precised, the main branch will be pulled
PHRASEANET_PLUGINS="git@github.com:alchemy-fr/Phraseanet-plugin-webgallery.git(custom-branch)"
# Plugins are separated by spaces
PHRASEANET_PLUGINS="git@github.com:foo/bar.git(branch-1) git@github.com:baz/42.git"
```
If you install private plugins, make sure you export your SSH private key content in order to allow docker build to access the GIT repository:
```bash
export PHRASEANET_SSH_PRIVATE_KEY=$(cat ~/.ssh/id_rsa)
# or if your private key is protected by a passphrase:
export PHRASEANET_SSH_PRIVATE_KEY=$(openssl rsa -in ~/.ssh/id_rsa -out /tmp/id_rsa_raw && cat /tmp/id_rsa_raw && rm /tmp/id_rsa_raw)
```
# With Vagrant (deprecated) # With Vagrant (deprecated)
## Development : ## Development :

View File

@@ -10,25 +10,34 @@ services:
gateway: gateway:
volumes: volumes:
- ../:/var/alchemy
- .:/var/alchemy/Phraseanet - .:/var/alchemy/Phraseanet
- ./docker/nginx/root/entrypoint.sh:/entrypoint.sh - ./docker/nginx/root/entrypoint.sh:/entrypoint.sh
- ${PHRASEANET_DATA_DIR}:/var/alchemy/Phraseanet/datas:rw - ${PHRASEANET_DATA_DIR}:/var/alchemy/Phraseanet/datas:rw
- ${PHRASEANET_THUMBNAILS_DIR}:/var/alchemy/Phraseanet/www/thumbnails:rw - ${PHRASEANET_THUMBNAILS_DIR}:/var/alchemy/Phraseanet/www/thumbnails:rw
- ${PHRASEANET_TMP_DIR}:/var/alchemy/Phraseanet/tmp:rw - ${PHRASEANET_TMP_DIR}:/var/alchemy/Phraseanet/tmp:rw
builder: builder:
build: build:
context: . context: .
target: builder target: builder
command: exit 0 args:
- SSH_PRIVATE_KEY=${PHRASEANET_SSH_PRIVATE_KEY}
- PHRASEANET_PLUGINS=${PHRASEANET_PLUGINS}
stdin_open: true
tty: true
volumes: volumes:
- ../:/var/alchemy
- .:/var/alchemy/Phraseanet - .:/var/alchemy/Phraseanet
- ${PHRASEANET_CONFIG_DIR}:/var/alchemy/Phraseanet/config:rw - ${PHRASEANET_CONFIG_DIR}:/var/alchemy/Phraseanet/config:rw
- ${PHRASEANET_LOGS_DIR}:/var/alchemy/Phraseanet/logs:rw - ${PHRASEANET_LOGS_DIR}:/var/alchemy/Phraseanet/logs:rw
- ${PHRASEANET_DATA_DIR}:/var/alchemy/Phraseanet/datas:rw - ${PHRASEANET_DATA_DIR}:/var/alchemy/Phraseanet/datas:rw
- ${PHRASEANET_THUMBNAILS_DIR}:/var/alchemy/Phraseanet/www/thumbnails:rw - ${PHRASEANET_THUMBNAILS_DIR}:/var/alchemy/Phraseanet/www/thumbnails:rw
- ${SSH_AUTH_SOCK}:/ssh-auth-sock
- ${HOME}/.ssh:/home/app/.ssh
- dev_vol:/home/app
environment:
- PHRASEANET_PROJECT_NAME
phraseanet: phraseanet:
environment: environment:
@@ -36,6 +45,7 @@ services:
- XDEBUG_CONFIG=remote_host=${XDEBUG_REMOTE_HOST} idekey=${IDE_KEY} remote_enable=1 profiler_enable=${XDEBUG_PROFILER_ENABLED} profiler_output_dir=/var/alchemy/Phraseanet/cache/profiler - XDEBUG_CONFIG=remote_host=${XDEBUG_REMOTE_HOST} idekey=${IDE_KEY} remote_enable=1 profiler_enable=${XDEBUG_PROFILER_ENABLED} profiler_output_dir=/var/alchemy/Phraseanet/cache/profiler
- PHP_IDE_CONFIG - PHP_IDE_CONFIG
volumes: volumes:
- ../:/var/alchemy
- .:/var/alchemy/Phraseanet - .:/var/alchemy/Phraseanet
- ${PHRASEANET_CONFIG_DIR}:/var/alchemy/Phraseanet/config:rw - ${PHRASEANET_CONFIG_DIR}:/var/alchemy/Phraseanet/config:rw
- ${PHRASEANET_LOGS_DIR}:/var/alchemy/Phraseanet/logs:rw - ${PHRASEANET_LOGS_DIR}:/var/alchemy/Phraseanet/logs:rw
@@ -45,6 +55,7 @@ services:
worker: worker:
volumes: volumes:
- ../:/var/alchemy
- .:/var/alchemy/Phraseanet - .:/var/alchemy/Phraseanet
- ${PHRASEANET_CONFIG_DIR}:/var/alchemy/Phraseanet/config:rw - ${PHRASEANET_CONFIG_DIR}:/var/alchemy/Phraseanet/config:rw
- ${PHRASEANET_LOGS_DIR}:/var/alchemy/Phraseanet/logs:rw - ${PHRASEANET_LOGS_DIR}:/var/alchemy/Phraseanet/logs:rw
@@ -72,8 +83,17 @@ services:
volumes: volumes:
- ${PHRASEANET_ELASTICSEARCH_DIR}:/usr/share/elasticsearch/data:rw - ${PHRASEANET_ELASTICSEARCH_DIR}:/usr/share/elasticsearch/data:rw
kibana:
image: kibana:4.6.6
ports:
- 5601:5601
networks: networks:
default: default:
ipam: ipam:
config: config:
- subnet: $PHRASEANET_SUBNET_IPS - subnet: $PHRASEANET_SUBNET_IPS
volumes:
dev_vol:
driver: local

View File

@@ -5,6 +5,9 @@ services:
build: build:
context: . context: .
target: phraseanet-nginx target: phraseanet-nginx
args:
- SSH_PRIVATE_KEY=${PHRASEANET_SSH_PRIVATE_KEY}
- PHRASEANET_PLUGINS=${PHRASEANET_PLUGINS}
image: $PHRASEANET_DOCKER_REGISTRY/phraseanet-nginx:$PHRASEANET_DOCKER_TAG image: $PHRASEANET_DOCKER_REGISTRY/phraseanet-nginx:$PHRASEANET_DOCKER_TAG
restart: on-failure restart: on-failure
volumes: volumes:
@@ -21,6 +24,9 @@ services:
build: build:
context: . context: .
target: phraseanet-fpm target: phraseanet-fpm
args:
- SSH_PRIVATE_KEY=${PHRASEANET_SSH_PRIVATE_KEY}
- PHRASEANET_PLUGINS=${PHRASEANET_PLUGINS}
image: $PHRASEANET_DOCKER_REGISTRY/phraseanet-fpm:$PHRASEANET_DOCKER_TAG image: $PHRASEANET_DOCKER_REGISTRY/phraseanet-fpm:$PHRASEANET_DOCKER_TAG
restart: on-failure restart: on-failure
depends_on: depends_on:
@@ -29,6 +35,7 @@ services:
- rabbitmq - rabbitmq
- elasticsearch - elasticsearch
environment: environment:
- PHRASEANET_PROJECT_NAME
- MAX_BODY_SIZE - MAX_BODY_SIZE
- MAX_INPUT_VARS - MAX_INPUT_VARS
- OPCACHE_ENABLED - OPCACHE_ENABLED
@@ -57,6 +64,9 @@ services:
build: build:
context: . context: .
target: phraseanet-worker target: phraseanet-worker
args:
- SSH_PRIVATE_KEY=${PHRASEANET_SSH_PRIVATE_KEY}
- PHRASEANET_PLUGINS=${PHRASEANET_PLUGINS}
image: $PHRASEANET_DOCKER_REGISTRY/phraseanet-worker:$PHRASEANET_DOCKER_TAG image: $PHRASEANET_DOCKER_REGISTRY/phraseanet-worker:$PHRASEANET_DOCKER_TAG
restart: on-failure restart: on-failure
depends_on: depends_on:
@@ -65,6 +75,7 @@ services:
- rabbitmq - rabbitmq
- elasticsearch - elasticsearch
environment: environment:
- PHRASEANET_PROJECT_NAME
- MAX_BODY_SIZE - MAX_BODY_SIZE
- MAX_INPUT_VARS - MAX_INPUT_VARS
- OPCACHE_ENABLED - OPCACHE_ENABLED

View File

@@ -0,0 +1,7 @@
local ret_status="%(?:%{$fg_bold[green]%}➜ :%{$fg_bold[red]%}➜ %s)"
PROMPT='%* %{$fg_bold[green]%}%n%{$fg[grey]%}@%m%{$fg_bold[green]%}%u ${ret_status}%{$fg_bold[green]%}%p %{$fg[cyan]%}%c %{$fg_bold[blue]%}$(git_prompt_info)%{$fg_bold[blue]%} % %{$reset_color%}'
ZSH_THEME_GIT_PROMPT_PREFIX="[%{$fg[red]%}"
ZSH_THEME_GIT_PROMPT_SUFFIX="%{$reset_color%}"
ZSH_THEME_GIT_PROMPT_DIRTY="%{$fg[blue]%}] %{$fg[yellow]%}✗%{$reset_color%}"
ZSH_THEME_GIT_PROMPT_CLEAN="%{$fg[blue]%}]"

View File

@@ -0,0 +1,56 @@
export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8
export ZSH=$HOME/.oh-my-zsh
ZSH_THEME="alchemy"
# Uncomment the following line to use case-sensitive completion.
# CASE_SENSITIVE="true"
# Uncomment the following line to use hyphen-insensitive completion. Case
# sensitive completion must be off. _ and - will be interchangeable.
# HYPHEN_INSENSITIVE="true"
# Uncomment the following line to disable bi-weekly auto-update checks.
# DISABLE_AUTO_UPDATE="true"
# Uncomment the following line to change how often to auto-update (in days).
# export UPDATE_ZSH_DAYS=13
# Uncomment the following line to disable colors in ls.
# DISABLE_LS_COLORS="true"
# Uncomment the following line to disable auto-setting terminal title.
# DISABLE_AUTO_TITLE="true"
# Uncomment the following line to enable command auto-correction.
# ENABLE_CORRECTION="true"
# Uncomment the following line to display red dots whilst waiting for completion.
# COMPLETION_WAITING_DOTS="true"
# Uncomment the following line if you want to disable marking untracked files
# under VCS as dirty. This makes repository status check for large repositories
# much, much faster.
# DISABLE_UNTRACKED_FILES_DIRTY="true"
# Uncomment the following line if you want to change the command execution time
# stamp shown in the history command output.
# The optional three formats: "mm/dd/yyyy"|"dd.mm.yyyy"|"yyyy-mm-dd"
# HIST_STAMPS="mm/dd/yyyy"
# Would you like to use another custom folder than $ZSH/custom?
# ZSH_CUSTOM=/path/to/new-custom-folder
# Which plugins would you like to load? (plugins can be found in ~/.oh-my-zsh/plugins/*)
# Custom plugins may be added to ~/.oh-my-zsh/custom/plugins/
# Example format: plugins=(rails git textmate ruby lighthouse)
# Add wisely, as too many plugins slow down shell startup.
plugins=(git symfony2)
# User configuration
source $ZSH/oh-my-zsh.sh
alias ll='ls -alFh'

View File

@@ -0,0 +1,5 @@
#!/bin/bash
if [ ! -d "$HOME/.oh-my-zsh" ]; then
cp -r "/bootstrap/.oh-my-zsh" "$HOME/.oh-my-zsh"
fi

View File

@@ -0,0 +1,7 @@
#!/bin/bash
ZSH_FILE="$HOME/.zshrc"
if [ ! -f "$HOME/.zshrc" ]; then
cp "/bootstrap/.zshrc" "$HOME/.zshrc"
fi

View File

@@ -0,0 +1,17 @@
#!/bin/bash
if [ -d /bootstrap/entrypoint.d ]; then
for i in /bootstrap/entrypoint.d/*.sh; do
if [ -r $i ]; then
. $i
fi
done
unset i
fi
if [ ! -t 1 ] ; then
echo "No tty available."
exit 0
fi
exec "$@"

View File

@@ -26,4 +26,6 @@ if [ ${XDEBUG_ENABLED} == "1" ]; then
docker-php-ext-enable xdebug docker-php-ext-enable xdebug
fi fi
./docker/phraseanet/plugins/console init
bash -e docker-php-entrypoint $@ bash -e docker-php-entrypoint $@

View File

@@ -935,7 +935,7 @@ cli_server.color = On
[Date] [Date]
; Defines the default timezone used by the date functions ; Defines the default timezone used by the date functions
; http://php.net/date.timezone ; http://php.net/date.timezone
date.timezone = Europe/Paris date.timezone = UTC
; http://php.net/date.default-latitude ; http://php.net/date.default-latitude
;date.default_latitude = 31.7667 ;date.default_latitude = 31.7667

View File

@@ -0,0 +1,29 @@
<?php
namespace Alchemy\Docker\Plugins\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class InitCommand extends Command
{
protected function configure()
{
$this
->setName('init')
->setDescription('Initialize plugins');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
foreach (glob('./plugins/*') as $dir) {
if (is_dir($dir)) {
$output->writeln(sprintf('Init <info>%s</info> plugin', basename($dir)));
SubCommand::run(sprintf('bin/setup plugin:add %s', $dir));
}
}
return 0;
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace Alchemy\Docker\Plugins\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class InstallCommand extends Command
{
protected function configure()
{
$this
->setName('install')
->setDescription('Install plugins');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$plugins = trim(getenv('PHRASEANET_PLUGINS'));
if (empty($plugins)) {
$output->writeln('<comment>No plugin to install... SKIP</comment>');
return 0;
}
$pluginsDir = 'plugins';
if (!is_dir($pluginsDir)) {
mkdir($pluginsDir);
}
foreach (explode(' ', $plugins) as $key => $plugin) {
$plugin = trim($plugin);
$repo = $plugin;
$branch = 'master';
if (1 === preg_match('#^(.+)\(([^)]+)\)$#', $plugin, $matches)) {
$repo = $matches[1];
$branch = $matches[2];
}
$pluginPath = './plugin' . $key;
if (is_dir($pluginPath)) {
SubCommand::run(sprintf('rm -rf %s', $pluginPath));
}
$output->writeln(sprintf('Installing <info>%s</info> (branch: <info>%s</info>)', $repo, $branch));
SubCommand::run(sprintf('git clone --single-branch --branch %s %s %s', $branch, $repo, $pluginPath));
$manifestSrc = $pluginPath.'/manifest.json';
if (!file_exists($manifestSrc)) {
throw new \Exception(sprintf('Cannot install plugin %s: no manifest.json file found', $plugin));
}
$pluginDestName = json_decode(file_get_contents($manifestSrc), true)['name'];
rename($pluginPath, $pluginsDir.'/'.$pluginDestName);
$pluginPath = $pluginsDir.'/'.$pluginDestName;
if (file_exists($pluginPath.'/composer.json')) {
SubCommand::run(sprintf('cd %s && composer install --no-dev', $pluginPath));
}
}
return 0;
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace Alchemy\Docker\Plugins\Command;
function setupStreaming()
{
ini_set('output_buffering', 'off');
ini_set('zlib.output_compression', false);
if (function_exists('apache_setenv')) {
apache_setenv('no-gzip', '1');
apache_setenv('dont-vary', '1');
}
}
setupStreaming();
abstract class SubCommand
{
static public function run($cmd)
{
system($cmd, $return);
if (0 !== $return) {
throw new \Exception(sprintf('Error %d: %s', $return, $cmd));
}
}
}

View File

@@ -0,0 +1,17 @@
#!/usr/bin/env php
<?php
namespace Alchemy\Docker\Plugins\Command;
require __DIR__.'/../../../vendor/autoload.php';
require __DIR__.'/SubCommand.php';
require __DIR__.'/InstallCommand.php';
require __DIR__.'/InitCommand.php';
use Symfony\Component\Console\Application;
$application = new Application();
$application->add(new InstallCommand());
$application->add(new InitCommand());
$application->run();

View File

@@ -15,4 +15,4 @@ if [ ${XDEBUG_ENABLED} == "1" ]; then
docker-php-ext-enable xdebug docker-php-ext-enable xdebug
fi fi
runuser -u app "$@" runuser -u app -- $@

View File

@@ -15,6 +15,21 @@ use Alchemy\Phrasea\Command\Command;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
function normalizePath($path) {
return array_reduce(explode('/', $path), function ($a, $b) {
if($a === 0)
$a = '/';
if($b === '' || $b === '.')
return $a;
if($b === '..')
return dirname($a);
return preg_replace('/\/+/', '/', "$a/$b");
}, 0);
}
abstract class AbstractPluginCommand extends Command abstract class AbstractPluginCommand extends Command
{ {
protected function validatePlugins(InputInterface $input, OutputInterface $output) protected function validatePlugins(InputInterface $input, OutputInterface $output)
@@ -54,33 +69,42 @@ abstract class AbstractPluginCommand extends Command
protected function doInstallPlugin($source, InputInterface $input, OutputInterface $output) protected function doInstallPlugin($source, InputInterface $input, OutputInterface $output)
{ {
$temporaryDir = $this->container['temporary-filesystem']->createTemporaryDirectory();
$output->write("Importing <info>$source</info>...");
$this->container['plugins.importer']->import($source, $temporaryDir);
$output->writeln(" <comment>OK</comment>");
$output->write("Validating plugin..."); $output->write("Validating plugin...");
$manifest = $this->container['plugins.plugins-validator']->validatePlugin($temporaryDir); $manifest = $this->container['plugins.plugins-validator']->validatePlugin($source);
$output->writeln(" <comment>OK</comment> found <info>".$manifest->getName()."</info>"); $output->writeln(" <comment>OK</comment> found <info>".$manifest->getName()."</info>");
$targetDir = $this->container['plugin.path'] . DIRECTORY_SEPARATOR . $manifest->getName(); $targetDir = $this->container['plugin.path'] . DIRECTORY_SEPARATOR . $manifest->getName();
if (normalizePath($targetDir) !== normalizePath($source)) {
$temporaryDir = $this->container['temporary-filesystem']->createTemporaryDirectory();
$output->write("Importing <info>$source</info>...");
$this->container['plugins.importer']->import($source, $temporaryDir);
$output->writeln(" <comment>OK</comment>");
$workingDir = $temporaryDir;
} else {
$workingDir = $targetDir;
}
$output->write("Setting up composer..."); if (!is_dir($workingDir.'/vendor')) {
$this->container['plugins.composer-installer']->install($temporaryDir); $output->write("Setting up composer...");
$output->writeln(" <comment>OK</comment>"); $this->container['plugins.composer-installer']->install($workingDir);
$output->writeln(" <comment>OK</comment>");
}
$output->write("Installing plugin <info>".$manifest->getName()."</info>..."); $output->write("Installing plugin <info>".$manifest->getName()."</info>...");
$this->container['filesystem']->mirror($temporaryDir, $targetDir); if (isset($temporaryDir)) {
$this->container['filesystem']->mirror($temporaryDir, $targetDir);
}
$output->writeln(" <comment>OK</comment>"); $output->writeln(" <comment>OK</comment>");
$output->write("Copying public files <info>".$manifest->getName()."</info>..."); $output->write("Copying public files <info>".$manifest->getName()."</info>...");
$this->container['plugins.assets-manager']->update($manifest); $this->container['plugins.assets-manager']->update($manifest);
$output->writeln(" <comment>OK</comment>"); $output->writeln(" <comment>OK</comment>");
$output->write("Removing temporary directory..."); if (isset($temporaryDir)) {
$this->container['filesystem']->remove($temporaryDir); $output->write("Removing temporary directory...");
$output->writeln(" <comment>OK</comment>"); $this->container['filesystem']->remove($temporaryDir);
$output->writeln(" <comment>OK</comment>");
}
$output->write("Activating plugin..."); $output->write("Activating plugin...");
$this->container['conf']->set(['plugins', $manifest->getName(), 'enabled'], true); $this->container['conf']->set(['plugins', $manifest->getName(), 'enabled'], true);

View File

@@ -69,7 +69,7 @@ class AddPluginTest extends PluginCommandTestCase
// the plugin is checked when updating config files // the plugin is checked when updating config files
self::$DI['cli']['plugins.plugins-validator']->expects($this->at(0)) self::$DI['cli']['plugins.plugins-validator']->expects($this->at(0))
->method('validatePlugin') ->method('validatePlugin')
->with('tempdir') ->with('TestPlugin')
->will($this->returnValue($manifest)); ->will($this->returnValue($manifest));
self::$DI['cli']['plugins.plugins-validator']->expects($this->at(1)) self::$DI['cli']['plugins.plugins-validator']->expects($this->at(1))

View File

@@ -5305,10 +5305,14 @@ jquery-simplecolorpicker@^0.3.1:
resolved "https://registry.yarnpkg.com/jquery-simplecolorpicker/-/jquery-simplecolorpicker-0.3.1.tgz#4f6befd380ab05470f585d5482e5180556e460eb" resolved "https://registry.yarnpkg.com/jquery-simplecolorpicker/-/jquery-simplecolorpicker-0.3.1.tgz#4f6befd380ab05470f585d5482e5180556e460eb"
integrity sha1-T2vv04CrBUcPWF1UguUYBVbkYOs= integrity sha1-T2vv04CrBUcPWF1UguUYBVbkYOs=
"jquery-treeview@git+https://github.com/alchemy-fr/jquery-treeview.git", "jquery-treeview@git+https://github.com/alchemy-fr/jquery-treeview.git#1e9e5a49d2875b878801e904cd08c2d25e85af1e": "jquery-treeview@git+https://github.com/alchemy-fr/jquery-treeview.git#1e9e5a49d2875b878801e904cd08c2d25e85af1e":
version "1.4.2"
resolved "git+https://github.com/alchemy-fr/jquery-treeview.git#1e9e5a49d2875b878801e904cd08c2d25e85af1e"
"jquery-treeview@https://github.com/alchemy-fr/jquery-treeview.git":
version "1.4.2" version "1.4.2"
uid "1e9e5a49d2875b878801e904cd08c2d25e85af1e" uid "1e9e5a49d2875b878801e904cd08c2d25e85af1e"
resolved "git+https://github.com/alchemy-fr/jquery-treeview.git#1e9e5a49d2875b878801e904cd08c2d25e85af1e" resolved "https://github.com/alchemy-fr/jquery-treeview.git#1e9e5a49d2875b878801e904cd08c2d25e85af1e"
jquery-ui-datepicker-with-i18n@^1.10.4: jquery-ui-datepicker-with-i18n@^1.10.4:
version "1.10.4" version "1.10.4"