mozart icon indicating copy to clipboard operation
mozart copied to clipboard

Root directories can not be deleted

Open darthvader666uk opened this issue 4 years ago • 21 comments

Hi,

Wondered if you could help?

Currently installing Mozart to our Project but every time I run vendor/bin/mozart compose I keep getting the following:

In Filesystem.php line 248:

  Root directories can not be deleted.  


compose

Im using WIndiows with VSCode and Git Bash. This is the composer file:

    "scripts": {
        "post-install-cmd": [
            "\"vendor/bin/mozart\" compose",
            "COMPOSER=composer-premium.json composer dump-autoload"
        ],
        "post-update-cmd": [
            "\"vendor/bin/mozart\" compose",
            "COMPOSER=composer-premium.json composer dump-autoload"
        ]
    },
    "extra": {
        "mozart": {
            "dep_namespace": "Vendor\\",
            "dep_directory": "/dist/",
            "classmap_directory": "/classes/dependencies/",
            "classmap_prefix": "UDP_",
            "packages": [
                "aws/aws-sdk-php"
            ]
        }
    }

I couldn't find much on google. Any help is appreciated.

darthvader666uk avatar Apr 30 '20 13:04 darthvader666uk

I think I've seen that when running Mozart from the vendor/bin directory without setting the current working directory to be the project root. In my case through a PhpStorm run configuration on MacOS. I can't reproduce now and that doesn't sound like your problem, anyway.

Filesystem.php:248 is the throw here:

public function deleteDir($dirname)
{
    $dirname = Util::normalizePath($dirname);

    if ($dirname === '') {
        throw new RootViolationException('Root directories can not be deleted.');
    }

$dirname is your composer.json's dep_directory and classmap_prefix.

So Util::normalizePath must be returning an empty string.

I've set a breakpoint in PhpStorm and I'm not able to reproduce.

Try create a file 43.php in your project root containing:

<?php

use League\Flysystem\Util;

require_once './vendor/league/flysystem/src/Util.php';

$dep_directory = "/dist/";
$classmap_directory = "/classes/dependencies/";

echo 'Input: ' . $dep_directory . ' Output: ' . Util::normalizePath($dep_directory) . "\n";
echo 'Input: ' . $classmap_directory . ' Output: ' . Util::normalizePath($classmap_directory) . "\n";

Then php -f 43.php

My output is:

Input: /dist/ Output: dist
Input: /classes/dependencies/ Output: classes/dependencies

The other suspicion I had (but doesn't really make sense after reading the code) is that Windows is actually trying to delete a root dir so you should just change "dep_directory": "/dist/" to "dep_directory": "dist/".

BrianHenryIE avatar May 22 '20 00:05 BrianHenryIE

@BrianHenryIE Thank you very much for the detailed reply.

So I added the script and ran it and I get the following:

 php -f 43.php
Input: /dist/ Output: dist
Input: /classes/dependencies/ Output: classes/dependencies

Which Looked Identical to yours. SO I tweaked the dep_directory to "dep_directory": "dist/" and exactly the same:

"vendor/bin/mozart" compose

In Filesystem.php line 248:

  Root directories can not be deleted.


compose

Script "vendor/bin/mozart" compose handling the post-install-cmd event returned with error code 1
composer install failed. Aborting.

Im pretty sure its something Windows based but cant for the life of me figure out why its doing it

darthvader666uk avatar May 22 '20 14:05 darthvader666uk

Post your full composer.json, the smallest possible to reproduce it, and I'll try again.

BrianHenryIE avatar May 23 '20 05:05 BrianHenryIE

@BrianHenryIE I figured out the error, I was missing "delete_vendor_directories": false however, I appeared to have come into another error:

vendor/bin/mozart compose

Fatal error: Uncaught Error: Class 'Symfony\Component\Finder\Finder' not found in vendor\coenjacobs\mozart\src\Mover.php:54
Stack trace:
#0 vendor\coenjacobs\mozart\src\Console\Commands\Compose.php(96): CoenJacobs\Mozart\Mover->movePackage(Object(CoenJacobs\Mozart\Composer\Package))
#1 vendor\coenjacobs\mozart\src\Console\Commands\Compose.php(92): CoenJacobs\Mozart\Console\Commands\Compose->movePackage(Object(CoenJacobs\Mozart\Composer\Package))
#2 vendor\coenjacobs\mozart\src\Console\Commands\Compose.php(92): CoenJacobs\Mozart\Console\Commands\Compose->movePackage(Object(CoenJacobs\Mozart\Composer\Package))
#3 vendor\coenjacobs\mozart\src\Console\Commands\Compose.php(69): CoenJacobs\Mozart\Console\Commands\Compose->movePackage(Object(CoenJacobs\Mozart\Composer\Package))
#4 vendor\coenjacobs\mozart\src\Console\Commands\Compose.php(49): CoenJacobs\Mozart\Console\Commands\Compose->movePackages(Array)
#5 D: in vendor\coenjacobs\mozart\src\Mover.php on line 54

This is now my Composer file:

{
    "type": "project",
	"autoload": {
		"classmap": [ "vendor/symfony/event-dispatcher/", "vendor/psr/log/" ]
    },
    "extra": {
        "mozart": {
            "dep_namespace": "Mozart\\",
            "dep_directory": "/dist/vendor/",
            "classmap_directory": "/dist/vendor/",
            "classmap_prefix": "UDP_",
            "packages": [
                "aws/aws-sdk-php"
            ],
            "delete_vendor_directories": false
        }
    },
	"require": {
		"eher/oauth": "^1.0",
		"rackspace/php-opencloud": "1.12.2",
		"aws/aws-sdk-php": "2.8.*",
		"guzzle/guzzle": "3.9.3",
		"phpseclib/phpseclib": "1.0.*",
        "components/jquery-blockui": "*",
        "coenjacobs/mozart": "*"
    },
    "require-dev": {
		"squizlabs/php_codesniffer": "3.5.*",
        "phpcompatibility/php-compatibility": "9.3.*",
        "wp-coding-standards/wpcs": "2.1.*",
        "dealerdirect/phpcodesniffer-composer-installer": "0.5.*",
        "sirbrillig/phpcs-variable-analysis": "2.7.*"
    },
    "prefer-stable" : true
}

Any Idea?

darthvader666uk avatar Jun 04 '20 08:06 darthvader666uk

@BrianHenryIE An Update to what I posted, Looks like running vendor/bin/mozart compose removed the entire vendor folder even when "delete_vendor_directories": false is set. How odd.

Update:

Found that Issue too. I have a specific Composer file called composer-premium.json and I run it like this:

COMPOSER=composer-premium.json composer install

Looks Like Mozart is Expecting a composer.json file instead. without creating one, is there a way to specify to the composer file?

Im Still seeing this error as it appears to be removing the vendor folder (when using composer.json while testing):

$ mozart-compose

Fatal error: Uncaught Error: Class 'Symfony\Component\Finder\Finder' not found in D:\work\wp-optimize\vendor\coenjacobs\mozart\src\Mover.php:54
Stack trace:
#0 D:\work\wp-optimize\vendor\coenjacobs\mozart\src\Console\Commands\Compose.php(96): CoenJacobs\Mozart\Mover->movePackage(Object(CoenJacobs\Mozart\Composer\Package))
#1 D:\work\wp-optimize\vendor\coenjacobs\mozart\src\Console\Commands\Compose.php(92): CoenJacobs\Mozart\Console\Commands\Compose->movePackage(Object(CoenJacobs\Mozart\Composer\Package))
#2 D:\work\wp-optimize\vendor\coenjacobs\mozart\src\Console\Commands\Compose.php(69): CoenJacobs\Mozart\Console\Commands\Compose->movePackage(Object(CoenJacobs\Mozart\Composer\Package))
#3 D:\work\wp-optimize\vendor\coenjacobs\mozart\src\Console\Commands\Compose.php(49): CoenJacobs\Mozart\Console\Commands\Compose->movePackages(Array)
#4 D:\work\wp-optimize\vendor\symfony\console\Command\Command.php(258): CoenJacobs\Mozart\Console\Commands\Compose->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\C in D:\work\wp-optimize\vendor\coenjacobs\mozart\src\Mover.php on line 54

darthvader666uk avatar Jun 04 '20 08:06 darthvader666uk

Are you sure you're on the latest beta? I think unless you set "minimum-stability": "dev" (maybe "beta", but "dev" works) in your composer.json, an update will continue just installing 0.5.1 right now. Easiest to specify the version outright rather than rely on *.

I did do some testing the other day (the day before your last post) but I still wasn't able to reproduce your issue. Again, I'm on MacOS. Although maybe this will mostly boil down to delete_vendor_directories.

BrianHenryIE avatar Jun 05 '20 23:06 BrianHenryIE

Thank you @BrianHenryIE , I have added the "minimum-stability": "dev" to composer so that the latest version is updated.

I THINk you right that something is up with delete_vendor_directories as Regardless if I set it to true or false, it will always be removed.

However, Part of diagnosing that issue I THINK I found the Original issue when using Mozart that returned the following:

In Filesystem.php line 248:

  Root directories can not be deleted.  


compose

With a lot of diagnostic work, I figured out the issue. Basically you HAVE to use composer.json and not any other form. For some reason, Mozart, even though specifying the composer file, doesn't take it into consideration and of course, if composer.json, it isn't going to work.

New Issue

After figuring out you can only use composer.json I created the file with just the bits for Mozart to work.

When running I kept facing this issue:

Fatal error: Uncaught Error: Class 'Symfony\Component\Finder\Finder' not found in D:\work\wp-optimize\vendor\coenjacobs\mozart\src\Mover.php:57
Stack trace:
#0 D:\work\wp-optimize\vendor\coenjacobs\mozart\src\Console\Commands\Compose.php(126): CoenJacobs\Mozart\Mover->movePackage(Object(CoenJacobs\Mozart\Composer\Package))
#1 D:\work\wp-optimize\vendor\coenjacobs\mozart\src\Console\Commands\Compose.php(122): CoenJacobs\Mozart\Console\Commands\Compose->movePackage(Object(CoenJacobs\Mozart\Composer\Package))
#2 D:\work\wp-optimize\vendor\coenjacobs\mozart\src\Console\Commands\Compose.php(99): CoenJacobs\Mozart\Console\Commands\Compose->movePackage(Object(CoenJacobs\Mozart\Composer\Package))
#3 D:\work\wp-optimize\vendor\coenjacobs\mozart\src\Console\Commands\Compose.php(77): CoenJacobs\Mozart\Console\Commands\Compose->movePackages(Array)
#4 D:\work\wp-optimize\vendor\symfony\console\Command\Command.php(258): CoenJacobs\Mozart\Console\Commands\Compose->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony in D:\work\wp-optimize\vendor\coenjacobs\mozart\src\Mover.php on line 57

Regardless if I set delete_vendor_directories to true or false, it appears it is removing the vendor directory and of course, removing Mozart (Along with Symfony etc.).

So I thought MAYBE if I set up the packages I wanted to replaCE WITH Mozart outside of vendor, perhaps that would resolve it.

One of the Packages we want to resolve is Pimple, SO I added that into a new folder called src/vendor (originally it was in vendor) and ran Mozart:

In Finder.php line 589:

  The "D:\WorkProject/src/vendor/Pimple/" directory does not exist.  


compose

Again, when running, even though delete_vendor_directories is set to false, it removes the files and then complains it's not there. Of course, it isn't, its moved and which is quite odd.

So that I wasnt going mad, I ran this on the Gitlab server (Is uses this Docker Image) too and could re-create the same output.

The One Thing I did notice (mainly from my local setup) is the path as it starts off Windows Based. Would this be the issue why everything gets removed?

This is now my composer.json file:

{
    "name": "updraftplus/mozart",
    "type": "project",
    "extra": {
		"mozart": {
			"dep_namespace": "WPO\\pimple",
			"dep_directory": "/src/vendor/",
			"classmap_directory": "/classes/dependencies/",
			"classmap_prefix": "WPO_",
			"packages": [
                "pimple/pimple"
            ],
            "delete_vendor_directories": false
		}
    },
    "require-dev": {
        "coenjacobs/mozart": "*"
    },
    "minimum-stability": "dev"
}

Thank you for the amazing help so far, at least i feel we are getting somewhere!

darthvader666uk avatar Jun 08 '20 11:06 darthvader666uk

Hey @BrianHenryIE Looks like I have jit a wall with Mozart. I just cant get it going at all. I have tried many ways to do this including keeping the Mozart install Separate from the Other Vendor files as they keep on getting removed (even though delete_vendor_directories is set to false) and nothing is being processed.

Is it the fact I am on Windows and I cant set it up in that environment?

This is now my composer.json file:

{
    "name": "updraftplus/mozart",
    "type": "project",
    "config": {
        "vendor-dir": "mozart/vendor"
    },
    "extra": {
		"mozart": {
			"dep_namespace": "udp\\",
			"dep_directory": "dist/vendor/",
			"classmap_directory": "classes/vendor/",
			"classmap_prefix": "UDP_",
            "delete_vendor_directories": false,
            "packages": [
                "aws/aws-sdk-php"
            ]
		}
    },
    "require-dev": {
        "coenjacobs/mozart": "0.4.0"
    }
}

Keeping it away from the main composer file so it doesn't remove itself (Mozart kept on disappearing if ran inside the vendor folder).

When I run this, this is the error I get:

Running Mozart ...

In Filesystem.php line 389:

  File not found at path: D:/work/updraftplus/vendor/symfony/event-dispatcher/CHANGELOG.md  


compose

I have tried the latest beta version, Master and an older version. Nothing. Im missing something simple im sure but it just doesnt run.

darthvader666uk avatar Jul 03 '20 07:07 darthvader666uk

@BrianHenryIE So I have dug deeper in this issue and I managed to get one of the members of the team (who has a Mac) to try my exact same setup and you know what? Worked first time without issues for him.

So there must be a bug somewhere for a Windows setup like im using that it basically ignores a lot of stuff including not to delete vendor etc.

Do you mind me digging in the code? see if I can notice something that may help?

darthvader666uk avatar Jul 23 '20 07:07 darthvader666uk

I made a patch that replaces all / with PHP's DIRECTORY_SEPARATOR which might be what's needed. You can test by adding this to your composer.json:

"require-dev": {
  "cweagans/composer-patches": "~1.0",
},
"extra": {
  "patches": {
    "coenjacobs/mozart": {
      "DIRECTORY_SEPARATOR": "https://github.com/coenjacobs/mozart/pull/61.patch"
    }
  }
}

BrianHenryIE avatar Aug 07 '20 05:08 BrianHenryIE

@BrianHenryIE Thank you for the patch but...Didnt work :/

So Ive been trying to diagnose what on earth is going on under the windows directory and so far I have gone to mozart\src\Mover.php under function movePackage inside if ($autoloader instanceof NamespaceAutoloader) { and when It comes to $finder->files()->in($source_path); the path has been removed (Yet I never specified for it to be removed) and get the following error:

In Finder.php line 589:

  The "vendor\psr/container\src/" directory does not exist.  

Im going to try and drill down see if I can find why its deleting before it gets to this point but something odd is happening with the paths

darthvader666uk avatar Aug 10 '20 10:08 darthvader666uk

@BrianHenryIE To add to my testing etc. today, This fix you made worked for our Gitlab Ci service running an Alpine Linux Docker image.

However, I just cant get it to work for Windows at all (Via Windows Git Bash). At least I can carry on via gitlab for now.

darthvader666uk avatar Aug 10 '20 15:08 darthvader666uk

So vendor\psr/container\src/ is maybe the telling piece in the puzzle. The PHP DIRECTORY_SEPARATOR constant (?) is there because Windows uses \ and everyone else uses /. My previous commit was trying to address that but from your post it seems I didn't cover every instance. I've pushed another commit for Mover.php which I think should use the correct \// in the case of psr/container. Give it a go and let me know how you get on.

By "give it a go" you shouldn't have to change anything, composer-patches freshly applies patches every time you run composer install/update

BrianHenryIE avatar Aug 11 '20 05:08 BrianHenryIE

@BrianHenryIE Yes! thats it, it worked :)

I tried going through it myself and I found a few but didnt get there but that update has done the job.

Phew! thank you so much for that!

darthvader666uk avatar Aug 11 '20 09:08 darthvader666uk

When will this be in the release @BrianHenryIE oir is it just keep an eye out?

Also, I wondered if you have anything special you used to get aws/aws-sdk-php working (if you have tried). After going through Mozart, it just doesnt appear to work :/

I expect its the way AWS is setup :)

darthvader666uk avatar Aug 19 '20 12:08 darthvader666uk

I'm not a maintainer of this project, just I use it a lot. Half the problems I've helped with are ones I've already dealt with myself, but then have taken the time to PR. Others are ones that have been easier for me to figure because I've read the code more than most, and helping is fun.

So it's up to @coenjacobs 's timeline when #61 gets merged. It's a relatively easy to read PR but there's a couple of spots open to improvement. And no tests (I don't even know how to write cross-platform tests).

Regards AWS, I haven't used it in any project myself (I have one AWS SNS project but it doesn't need the SDK), but on a quick glance, I see there is a files entry in the sdk's autoload key. I don't think Mozart handles an autoloader of type files. Best bet would be to start a new issue with minimal composer.json and hopefully it suits someone to look into it soon.

BrianHenryIE avatar Aug 20 '20 09:08 BrianHenryIE

Thank you for the reply @BrianHenryIE and thought you were one of the main people on the project as you have been very helpful :)

So yes, https://github.com/coenjacobs/mozart/pull/61 has solved this issues problem and shall wait on @coenjacobs but not the end of the world as Im pulling in the changes anyway which is cool.

With AWS yes, I noticed that too when digging around and seems to be a no go at the moment. I think I will raise another issue, see if we can dig deeper and look at whats going on

darthvader666uk avatar Aug 20 '20 11:08 darthvader666uk

@darthvader666uk You are correct, @BrianHenryIE is an amazing help to this project. I'll have a look over the next few days, as a couple PRs and issues are requiring my attention. I'll see to releasing everything in the next beta of 0.6.0 and get that into a stable release as soon as I deem things to be stable enough. Really glad this got sorted!

coenjacobs avatar Aug 20 '20 11:08 coenjacobs

@coenjacobs & @BrianHenryIE So Oddly, after the Patch got merged, I had this issue again:

In Filesystem.php line 389:

  File not found at path: D:/work/wp-optimize/dist/vendor/marcusschwarz/lesserphp/vendor/marcusschwarz/lesserphp/lessc.inc.php 

With it being Windows and how Paths can be funky, I have done alot of digging and I think I finally come up with a solution (working locally).

In src\Console\Commands\Compose.php on like 38 there is $this->workingDir = $workingDir;. I replace it with $this->workingDir = DIRECTORY_SEPARATOR; and it worked.

I had no issues at all. I think the combo with the updates from DIRECTORY_SEPARATOR seemed to have resolve almost all issues and I think that was the final piece.

Problem is, as its my Fork I got no way of testing it on out CI on Gitlab. I can make a Pull request but I expect you would want to check first etc.

What are your thoughts?

Edit: I did a pull request but this can alway be removed if your not happy but Im doing full test as we speak.

darthvader666uk avatar Nov 03 '20 14:11 darthvader666uk

I'm pretty sure the test suite won't pass with that change because I need to set the current working directory to a temporary directory to execute composer install and then test parts of Mozart in. Conceptually, getcwd() makes sense for $workingDir and I'm not exactly sure why \ is working even on Windows. I could see . working.

Anyway, this issue looks to be the same as #90, where str_replace was not matching because of cases where DIRECTORY_SEPARATOR was still not applied and consecutive slashes maybe also affected the match.

I posted some code in that issue to replace in Mover::moveFile() and I've expanded it a bit, so would you mind trying to replace your entire moveFile() function in Move.php:167 with the following:

public function moveFile(Package $package, $autoloader, $file, $path = '')
{

    /**
     * For Windows & Unix file paths' compatibility.
     *
     *  * Removes duplicate `\` and `/`.
     *  * Trims them from each end.
     *  * Replaces them with the OS agnostic DIRECTORY_SEPARATOR.
     *
     * @param string $path A full or partial filepath.
     *
     * @return string
     */
    $clean = function ($path) {
        return trim(preg_replace('/[\/\\\\]+/', DIRECTORY_SEPARATOR, $path), DIRECTORY_SEPARATOR);
    };

    // The relative path to the file from the project root.
    $sourceFilePath = $clean(str_replace($this->workingDir, '', $file->getPathname()));

    $namespacePath = $clean($package->config->name);

    if ($autoloader instanceof NamespaceAutoloader) {
        $namespacePath = $clean($autoloader->getNamespacePath());

        // TODO: $path should come from the NameSpaceAutoloader object.
        $packageVendorPath = 'vendor' . DIRECTORY_SEPARATOR . $namespacePath
                             . DIRECTORY_SEPARATOR . $clean($path);

        $mozartDepPath = $clean($this->config->dep_directory) . DIRECTORY_SEPARATOR . $namespacePath;

        $targetFilePath = str_ireplace($packageVendorPath, $mozartDepPath, $sourceFilePath);
    } else {
        $classmapDirectory = $clean($this->config->classmap_directory);

        $packageVendorPath = 'vendor' . DIRECTORY_SEPARATOR . $namespacePath;

        $mozartClassesPath = $classmapDirectory . DIRECTORY_SEPARATOR . $namespacePath;

        $targetFilePath = str_ireplace($packageVendorPath, $mozartClassesPath, $sourceFilePath);
    }

    $this->filesystem->copy($sourceFilePath, $targetFilePath);

    return $targetFilePath;
}

There's still some DIRECTORY_SEPARATOR cases outside that function to look at, but I think this might be what you need.

BrianHenryIE avatar Nov 25 '20 01:11 BrianHenryIE

The following appears correct:

{
  "minimum-stability": "dev",
  "prefer-stable" : true,
  "require": {
    "aws/aws-sdk-php": "2.8.31"
  },
  "require-dev": {
    "coenjacobs/mozart": "dev-master",
    "cweagans/composer-patches": "*"
  },
  "extra": {
    "patches": {
      "coenjacobs/mozart": {
        "Do not confuse classnames with namespaces": "https://github.com/coenjacobs/mozart/pull/100.patch",
        "Directory separator and its duplicates": "https://github.com/coenjacobs/mozart/pull/103.patch"
      }
    },
    "mozart": {
      "dep_namespace": "Mozart\\",
      "dep_directory": "/dep_directory/",
      "classmap_directory": "/classmap_directory/",
      "classmap_prefix": "Mozart_",
      "delete_vendor_directories": false,
      "override_autoload": {
        "guzzle/guzzle": {
          "psr-4": {
            "Guzzle": "src/"
          }
        }
      }
    }
  }
}

The override_autoload ~~maybe~~ would be unnecessary if I finished #63.

BrianHenryIE avatar Dec 14 '20 23:12 BrianHenryIE