Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into DIRECTORY_SEPARAT…
Browse files Browse the repository at this point in the history
…OR-and-its-duplicates
  • Loading branch information
BrianHenryIE committed Feb 7, 2021
2 parents f78e341 + 3b1243c commit 17a4502
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 49 deletions.
49 changes: 38 additions & 11 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,46 @@
name: Publish release to Docker Hub
name: Build, tag and attach releases

on:
push:
tags:
- "*.*.*"
release:
types: [published]

jobs:
create-phar:
runs-on: ubuntu-latest
name: Create Mozart phar
steps:
- uses: actions/checkout@v1

- name: Install PHP
uses: shivammathur/[email protected]
with:
php-version: 7.3

- name: Install dependencies
run: composer install --no-dev --prefer-dist --no-suggest --no-progress

- name: Create .phar
run: |
wget -O phar-composer.phar https://github.com/clue/phar-composer/releases/download/v1.2.0/phar-composer-1.2.0.phar
mkdir build
mv vendor build/vendor
mv src build/src
mv bin build/bin
mv composer.json build
php -d phar.readonly=off phar-composer.phar build ./build/
- name: Test run mozart
run: php mozart.phar --version

- uses: meeDamian/[email protected]
with:
token: ${{ secrets.GITHUB_TOKEN }}
files: mozart.phar
gzip: false
allow_override: true
docker:
runs-on: ubuntu-latest
name: Create Docker tag
steps:
- name: Checkout
uses: actions/checkout@v2
Expand All @@ -16,14 +49,8 @@ jobs:
id: prep
run: |
DOCKER_IMAGE=coenjacobs/mozart
VERSION=latest
if [[ $GITHUB_REF == refs/tags/* ]]; then
VERSION=${GITHUB_REF#refs/tags/v}
fi
VERSION=${{ github.event.release.tag_name }}
TAGS="${DOCKER_IMAGE}:${VERSION}"
if [[ $VERSION =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
TAGS="$TAGS,${DOCKER_IMAGE}:latest"
fi
echo ::set-output name=tags::${TAGS}
- name: Set up QEMU
Expand Down
58 changes: 54 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# Mozart [![Latest Stable Version](https://poser.pugx.org/coenjacobs/mozart/v/stable.svg)](https://packagist.org/packages/coenjacobs/mozart) [![License](https://poser.pugx.org/coenjacobs/mozart/license.svg)](https://packagist.org/packages/coenjacobs/mozart) [![Docker Image Pulls](https://img.shields.io/docker/pulls/coenjacobs/mozart.svg)](https://hub.docker.com/r/coenjacobs/mozart)
# Mozart [![Latest Stable Version](https://poser.pugx.org/coenjacobs/mozart/v/stable.svg)](https://packagist.org/packages/coenjacobs/mozart) [![License](https://poser.pugx.org/coenjacobs/mozart/license.svg)](https://packagist.org/packages/coenjacobs/mozart) [![Total Downloads](https://poser.pugx.org/coenjacobs/mozart/downloads)](//packagist.org/packages/coenjacobs/mozart) [![Docker Image Pulls](https://img.shields.io/docker/pulls/coenjacobs/mozart.svg)](https://hub.docker.com/r/coenjacobs/mozart)
Composes all dependencies as a package inside a WordPress plugin. Load packages through Composer and have them wrapped inside your own namespace. Gone are the days when plugins could load conflicting versions of the same package, resulting in hard to reproduce bugs.

This package requires PHP 7.3 or higher in order to run the tool. You can use the resulting files as a bundle, requiring any PHP version you like, even PHP 5.2.

**Warning:** This package is very experimental and breaking changes are very likely until version 1.0.0 is tagged. Use with caution, always wear a helmet when using this in production environments.

## Installation
Mozart brings its own dependencies to the table and that potentially introduces its own problems (yes, I realise how meta that is, for a package like this). That's why installing Mozart in isolation, either through the Docker container, the available PHAR file or installing Mozart as a global dependency with Composer is prefered. In all cases, the [configuration](#configuration) still needs to be placed in the `composer.json` file of the project iself.

### Docker
Pull the Docker image from the registry:
Expand All @@ -24,14 +25,49 @@ Above command automatically adds the current working directory as a volume into

Please note that the Docker image for Mozart is only available starting from the `latest` build of version 0.7.0. The `latest` tag is always the latest build of the `master` branch and not a stable version. You can see [all available tags on Docker Hub](https://hub.docker.com/r/coenjacobs/mozart/tags).

### PHAR (via Phive)
Mozart can be installed via [Phive](https://github.com/phar-io/phive):

```
phive install coenjacobs/mozart --force-accept-unsigned
```

Alternatively, the `mozart.phar` file can be [downloaded from the releases page](https://github.com/coenjacobs/mozart/releases) and then be run from your project directory:

```
php mozart.phar compose
```

### Composer
Install through Composer, only required in development environments:
To install Mozart and its dependencies, without conflicting with the dependencies of your project, it is recommended that you install Mozart as a global package, if you choose to install Mozart via Composer.

#### Global package
Using the `global` command when installing Mozart, it will be installed as a system wide package:

`composer require coenjacobs/mozart --dev`
```
composer global require coenjacobs/mozart
```

You can then find the bin file named `mozart` inside your `~/.composer/vendor/bin/` directory and run it from your project directory, referencing the full path to the bin file:

```
~/.composer/vendor/bin/mozart compose
```

#### Development dependency of your project
You can install through Composer in the project itself, only required in development environments:

```
composer require coenjacobs/mozart --dev
```

This gives you a bin file named `mozart` inside your `vendor/bin` directory, after loading the whole package inside your project. Try running `vendor/bin/mozart` to verify it works.

After configuring Mozart properly, the `mozart compose` command does all the magic.
After configuring Mozart properly, the `mozart compose` command does all the magic:

```
vendor/bin/mozart compose
```

## Configuration
Mozart requires little configuration. All you need to do is tell it where the bundled dependencies are going to be stored and what namespace they should be put inside. This configuration needs to be done in the `extra` property of your `composer.json` file:
Expand Down Expand Up @@ -94,3 +130,17 @@ Mozart is designed to install and be forgotten about. Using Composer scripts, th
]
}
```

When using Mozart through its Docker container, you can replace the `"\"vendor/bin/mozart\" compose",` lines with the actual commands you use to [run the Docker container](#docker) for your specific project. Running Mozart from inside the Docker container is really fast and shouldn't take more than a couple seconds.

## Background and philosophy
Mozart is designed to bridge the gap between the WordPress ecosytem and the vast packages ecosystem of PHP as a whole. Since WordPress is such an end-user friendly focussed CMS (for good reasons), there is no place within the ecosystem where an end-user would be burdened with using a developers tool like Composer. Also, since WordPress has to run on basically any hosting infrastructure, running Composer to install packages from the administration panel (trust me, I've tried - it can be done) is a mission impossible to make it happen and compatible with every server out there.

But why make a new tool for this? There are other tools that enable you to do this, right? Yes, there are now. [PHP-Scoper](https://github.com/humbug/php-scoper), for example. PHP-Scoper is a fantastic tool, that does the job right. But, PHP-Scoper wasn't available back when I started the Mozart project. Also, PHP-Scoper has a few limitations (no support for classmap autoloaders, for example) that were and are still quite common within the WordPress ecosystem. Finally, PHP-Scoper can be quite the tool to add to your development flow, while Mozart was designed to be _simple to implement_, specifically tailored for WordPress projects.

The key values of what's important to Mozart:
- Must be able to be easily installable by a developer, preferably in a specific version.
- Distribution must be done through an existing package manager or easily maintained separately.
- Shouldn't add a whole layer of complexity to the development process, i.e. learning a whole new tool/language.

Mozart always has been and always will be geared towards solving the conflicting dependencies problem in the WordPress ecosystem, as efficiently and opinionated as possible. By being opinionated in certain ways and specifically focussed on WordPress projects, Mozart has quickly become easy to understand and implement.
38 changes: 21 additions & 17 deletions bin/mozart
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
#!/usr/bin/env php
<?php
call_user_func(function ($version) {
if (is_file($autoload = getcwd() . '/vendor/autoload.php')) {
require $autoload;
} elseif (is_file($autoload = getcwd() . '/../../autoload.php')) {
require $autoload;
}
if( ! Phar::running() ) {
if (is_file($autoload = getcwd() . '/vendor/autoload.php')) {
require $autoload;
} elseif (is_file($autoload = getcwd() . '/../../autoload.php')) {
require $autoload;
}

if (is_file($autoload = __DIR__ . '/../vendor/autoload.php')) {
require($autoload);
} elseif (is_file($autoload = __DIR__ . '/../../../autoload.php')) {
require($autoload);
} else {
fwrite(STDERR,
'You must set up the project dependencies, run the following commands:' . PHP_EOL .
'curl -s http://getcomposer.org/installer | php' . PHP_EOL .
'php composer.phar install' . PHP_EOL
);
exit(1);
if ( is_file( $autoload = __DIR__ . '/../vendor/autoload.php' ) ) {
require( $autoload );
} elseif ( is_file( $autoload = __DIR__ . '/../../../autoload.php' ) ) {
require( $autoload );
} else {
fwrite( STDERR,
'You must set up the project dependencies, run the following commands:' . PHP_EOL .
'curl -s http://getcomposer.org/installer | php' . PHP_EOL .
'php composer.phar install' . PHP_EOL
);
exit( 1 );
}
} else {
include 'phar://mozart.phar/vendor/autoload.php';
}

$app = new CoenJacobs\Mozart\Console\Application($version);
$app->run();
}, '0.7.0');
}, '0.8.0');
2 changes: 0 additions & 2 deletions src/Console/Commands/Compose.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

namespace CoenJacobs\Mozart\Console\Commands;

use CoenJacobs\Mozart\Composer\Autoload\Classmap;
use CoenJacobs\Mozart\Composer\Autoload\NamespaceAutoloader;
use CoenJacobs\Mozart\Composer\Package;
use CoenJacobs\Mozart\Mover;
use CoenJacobs\Mozart\Replacer;
Expand Down
12 changes: 6 additions & 6 deletions tests/Console/Commands/ComposeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public function it_fails_gracefully_when_composer_json_absent(): void
$outputInterfaceMock->expects($this->exactly(1))
->method('write');

$compose = new class( $inputInterfaceMock, $outputInterfaceMock ) extends Compose {
new class( $inputInterfaceMock, $outputInterfaceMock ) extends Compose {
public function __construct($inputInterfaceMock, $outputInterfaceMock)
{
parent::__construct();
Expand Down Expand Up @@ -74,7 +74,7 @@ public function it_handles_malformed_json_with_grace(): void
$outputInterfaceMock->expects($this->exactly(1))
->method('write');

$compose = new class( $inputInterfaceMock, $outputInterfaceMock ) extends Compose {
new class( $inputInterfaceMock, $outputInterfaceMock ) extends Compose {
public function __construct($inputInterfaceMock, $outputInterfaceMock)
{
parent::__construct();
Expand Down Expand Up @@ -104,7 +104,7 @@ public function it_handles_absent_extra_config_with_grace(): void
$outputInterfaceMock->expects($this->exactly(1))
->method('write');

$compose = new class( $inputInterfaceMock, $outputInterfaceMock ) extends Compose {
new class( $inputInterfaceMock, $outputInterfaceMock ) extends Compose {
public function __construct($inputInterfaceMock, $outputInterfaceMock)
{
parent::__construct();
Expand Down Expand Up @@ -135,7 +135,7 @@ public function it_handles_malformed_extra_config_with_grace(): void
$outputInterfaceMock->expects($this->exactly(1))
->method('write');

$compose = new class( $inputInterfaceMock, $outputInterfaceMock ) extends Compose {
new class( $inputInterfaceMock, $outputInterfaceMock ) extends Compose {
public function __construct($inputInterfaceMock, $outputInterfaceMock)
{
parent::__construct();
Expand Down Expand Up @@ -165,7 +165,7 @@ public function it_handles_absent_mozart_config_with_grace(): void
$outputInterfaceMock->expects($this->exactly(1))
->method('write');

$compose = new class( $inputInterfaceMock, $outputInterfaceMock ) extends Compose {
new class( $inputInterfaceMock, $outputInterfaceMock ) extends Compose {
public function __construct($inputInterfaceMock, $outputInterfaceMock)
{
parent::__construct();
Expand Down Expand Up @@ -197,7 +197,7 @@ public function it_handles_malformed_mozart_config__with_grace(): void
$outputInterfaceMock->expects($this->exactly(1))
->method('write');

$compose = new class( $inputInterfaceMock, $outputInterfaceMock ) extends Compose {
new class( $inputInterfaceMock, $outputInterfaceMock ) extends Compose {
public function __construct($inputInterfaceMock, $outputInterfaceMock)
{
parent::__construct();
Expand Down
9 changes: 4 additions & 5 deletions tests/MoverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public function setUp(): void
if (!file_exists($this->testsWorkingDir)) {
mkdir($this->testsWorkingDir);
}

$config = new class() {
};
$config->dep_directory = "/dep_directory/";
Expand Down Expand Up @@ -93,7 +93,7 @@ public function it_is_unpertrubed_by_existing_dirs(): void

$this->assertDirectoryExists($this->testsWorkingDir . $this->config->dep_directory);
$this->assertDirectoryExists($this->testsWorkingDir . $this->config->classmap_directory);

$packages = array();

ob_start();
Expand All @@ -118,7 +118,6 @@ public function it_deletes_subdirs_for_packages_about_to_be_moved(): void
mkdir($this->testsWorkingDir . DIRECTORY_SEPARATOR . $this->config->dep_directory);
mkdir($this->testsWorkingDir . DIRECTORY_SEPARATOR . $this->config->classmap_directory);

// TODO: Create the subdirs that should be deleted.
mkdir($this->testsWorkingDir . DIRECTORY_SEPARATOR . $this->config->dep_directory . 'Pimple');
mkdir($this->testsWorkingDir . DIRECTORY_SEPARATOR . $this->config->classmap_directory . 'ezyang');

Expand Down Expand Up @@ -186,7 +185,7 @@ public function it_moves_each_file_once_per_namespace()

chdir($this->testsWorkingDir);

exec('composer install');
exec('composer update');

$inputInterfaceMock = $this->createMock(InputInterface::class);
$outputInterfaceMock = $this->createMock(OutputInterface::class);
Expand All @@ -209,7 +208,7 @@ public function it_moves_each_file_once_per_namespace()
public function tearDown(): void
{
parent::tearDown();

$dir = $this->testsWorkingDir;

$it = new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS);
Expand Down
4 changes: 2 additions & 2 deletions tests/replacers/ClassmapReplacerIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,14 @@ public function test_it_does_not_make_classname_replacement_inside_namespaced_fi

chdir($this->testsWorkingDir);

exec('composer install');
exec('composer update');

$inputInterfaceMock = $this->createMock(InputInterface::class);
$outputInterfaceMock = $this->createMock(OutputInterface::class);

$mozartCompose = new Compose();

$result = $mozartCompose->run($inputInterfaceMock, $outputInterfaceMock);
$mozartCompose->run($inputInterfaceMock, $outputInterfaceMock);

$php_string = file_get_contents($this->testsWorkingDir .'/dep_directory/BrianHenryIE/WP_Logger/class-logger.php');

Expand Down
4 changes: 2 additions & 2 deletions tests/replacers/NamespaceReplacerIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,14 @@ public function test_it_does_not_make_classname_replacement_inside_namespaced_fi

chdir($this->testsWorkingDir);

exec('composer install');
exec('composer update');

$inputInterfaceMock = $this->createMock(InputInterface::class);
$outputInterfaceMock = $this->createMock(OutputInterface::class);

$mozartCompose = new Compose();

$result = $mozartCompose->run($inputInterfaceMock, $outputInterfaceMock);
$mozartCompose->run($inputInterfaceMock, $outputInterfaceMock);

$mpdf_php = file_get_contents($this->testsWorkingDir .'/dep_directory/Mpdf/Mpdf.php');

Expand Down

0 comments on commit 17a4502

Please sign in to comment.