Composer で複数のパッケージをインストールしていて、開発を進めるうちにそれらの一部が不要になること、あると思います。そんなとき、どうやって不要になったパッケージを削除しているでしょうか?
幾つか方法はあると思うので、それらの動作を確認してみます。
まず、実験のために composer.json
を a b c d の 4 つのディレクトリに作成します。それぞれ下記の内容です。
{
"name": "oreore/a",
"require": {
"oreore/c": "~1.0"
}
}
{
"name": "oreore/b",
"require": {
"oreore/c": "~1.0",
"oreore/d": "~1.0"
}
}
{
"name": "oreore/c"
}
{
"name": "oreore/d"
}
それぞれのディレクトリを git リポジトリにして v1.0.0 タグを作成します。
for x in a b c d; do (
cd $x
git init
git add .
git commit -m first
git tag v1.0.0
); done
1つ上のディレクトリにも composer.json
を作成します。repositories
で a b c d の .git ディレクトリを指定します。
{
"repositories": [
{ "packagist": false },
{ "type": "vcs", "url": "file://./a/.git" },
{ "type": "vcs", "url": "file://./b/.git" },
{ "type": "vcs", "url": "file://./c/.git" },
{ "type": "vcs", "url": "file://./d/.git" }
],
"require": {
"oreore/a": "~1.0",
"oreore/b": "~1.0"
}
}
require
には a と b だけ記述していますが、a は c に依存しており、b は c と d に依存しているため、a b c d のすべてがインストールされます。
$ composer install
Loading composer repositories with package information
Installing dependencies (including require-dev)
- Installing oreore/c (v1.0.0)
Cloning 2d9272d949393688db7dc9438ed5ef5526d6378c
- Installing oreore/a (v1.0.0)
Cloning f48fd8b11e1fb71fb6638d1c765ab556da16e136
- Installing oreore/d (v1.0.0)
Cloning 2af7c6202d11209cd60e28e97b83d1686e1fd945
- Installing oreore/b (v1.0.0)
Cloning 0dc81980cf7e78c0541b5ed6df7cbd40b34efbe3
Writing lock file
Generating autoload files
依存関係のグラフは次のようになっています。
この状態から b が不要になったので b を削除します。
composer update
一番ありがちなのが composer.json
から不要になったパッケージを手で削除して composer update
だと思います。
{
"repositories": [
{ "packagist": false },
{ "type": "vcs", "url": "file://./a/.git" },
{ "type": "vcs", "url": "file://./b/.git" },
{ "type": "vcs", "url": "file://./c/.git" },
{ "type": "vcs", "url": "file://./d/.git" }
],
"require": {
"oreore/a": "~1.0"
}
}
$ composer update
Loading composer repositories with package information
Updating dependencies (including require-dev)
- Removing oreore/b (v1.0.0)
- Removing oreore/d (v1.0.0)
Writing lock file
Generating autoload files
しかしこれはあまり良くありません、例えば次のようにインストールしていたパッケージの次のバージョン v1.0.1 がリリースされていた場合、
for x in a b c d; do (
cd $x
touch README.md
git add .
git commit -m v1.0.1
git tag v1.0.1
); done
composer update
だと composer.lock
で固定されていたパッケージのバージョンが更新されてしまいます。
$ composer update
Loading composer repositories with package information
Updating dependencies (including require-dev)
- Removing oreore/b (v1.0.0)
- Removing oreore/d (v1.0.0)
- Updating oreore/c (v1.0.0 => v1.0.1)
Checking out afe5adc298e999dd4241306cf39f22a4a8bb7001
- Updating oreore/a (v1.0.0 => v1.0.1)
Checking out 3c1000a38b7e806f5a022ded8f8811c7b1e4d426
Writing lock file
Generating autoload files
composer update [package]
composer update
は引数でパッケージ名を指定できます。なので、composer.json
から不要になったパッケージを手で削除して、
{
:
"require": {
"oreore/a": "~1.0"
}
}
composer update
で不要になったパッケージ名を指定すると、そのパッケージの削除だけが行われます。
$ composer update oreore/b
Loading composer repositories with package information
Updating dependencies (including require-dev)
- Removing oreore/b (v1.0.0)
Writing lock file
Generating autoload files
がしかし、これだと b から依存していた d が削除されていません。b が不要なら d も不要なので一緒に削除したいです。
composer update --with-dependencies [package]
composer update
に --with-dependencies
オプションを追加すると、指定したパッケージから依存しているパッケージも一緒に更新されます。
まずは composer.json
を手で修正して、
{
:
"require": {
"oreore/a": "~1.0"
}
}
composer update --with-dependencies
で不要になったパッケージを指定します。
$ composer update --with-dependencies oreore/b
Loading composer repositories with package information
Updating dependencies (including require-dev)
- Removing oreore/b (v1.0.0)
- Removing oreore/d (v1.0.0)
- Updating oreore/c (v1.0.0 => v1.0.1)
Checking out afe5adc298e999dd4241306cf39f22a4a8bb7001
Writing lock file
Generating autoload files
b と d を両方とも削除することができました。がしかし、a と b の両方から依存している c が更新されてしまいました。
composer remove [package]
最近になって Composer に remove
というコマンドが追加されていることに気づきました。このコマンドを使うと composer.json
の修正も行ってくれるので composer.json
を手で修正する必要がありません。
$ composer remove oreore/b
Loading composer repositories with package information
Updating dependencies (including require-dev)
- Removing oreore/b (v1.0.0)
Writing lock file
Generating autoload files
がしかし、composer update
でパッケージ名を指定したときと同じように、b からしか依存されていない d が削除されませんでした。
composer remove --update-with-dependencies [package]
composer remove
には --update-with-dependencies
というオプションを付けることができます。これなら d も一緒に削除されそうです。
$ composer remove --update-with-dependencies oreore/b
Loading composer repositories with package information
Updating dependencies (including require-dev)
- Removing oreore/b (v1.0.0)
- Removing oreore/d (v1.0.0)
- Updating oreore/c (v1.0.0 => v1.0.1)
Checking out afe5adc298e999dd4241306cf39f22a4a8bb7001
Writing lock file
Generating autoload files
b と d の両方が削除されました。がしかし、composer update --with-dependencies
と同じように a と b の両方から依存されている c が更新されてしまいました。
composer remove ---no-update [package]
composer remove
には --no-update
というオプションを付けることができます。これなら更新は行われなさそうです。
$ composer remove --no-update oreore/b
ん? なにも起こらない?
$ cat composer.json
{
"repositories": [
{ "packagist": false },
{ "type": "vcs", "url": "file://./a/.git" },
{ "type": "vcs", "url": "file://./b/.git" },
{ "type": "vcs", "url": "file://./c/.git" },
{ "type": "vcs", "url": "file://./d/.git" }
],
"require": {
"oreore/a": "~1.0"
}
}
どうやら composer.json
を修正するだけになるようです。
まとめ
結局、どうやっても b と b からだけ依存されているパッケージをまとめて 削除だけ を行うことはできませんでした。
どしても b と d の削除だけを行いたければ、次のようにすると良いでしょう。
まずは composer remove --no-update
で composer.json
を修正します。
$ composer remove --no-update oreore/b
次に composer update --dry-run
を実行します。
$ composer update --dry-run
Loading composer repositories with package information
Updating dependencies (including require-dev)
- Uninstalling oreore/b (v1.0.0)
- Uninstalling oreore/d (v1.0.0)
- Updating oreore/c (v1.0.0) to oreore/c (v1.0.1)
- Updating oreore/a (v1.0.0) to oreore/a (v1.0.1)
すると、実際に composer update
を実行すると何が起こるかが表示されます。
Uninstalling
と表示されたパッケージを composer update
に羅列します。
$ composer update oreore/b oreore/d
Loading composer repositories with package information
Updating dependencies (including require-dev)
- Removing oreore/b (v1.0.0)
- Removing oreore/d (v1.0.0)
Writing lock file
Generating autoload files
不要になったパッケージの削除だけが行われます。
シェル関数にしてみました。
composer-uninstall () {
composer remove --no-update "$@"
composer update --dry-run |
grep -Eo -e '- Uninstalling\s+\S+' |
cut -d' ' -f3 |
xargs composer update
}
次のように実行します。
$ composer-uninstall oreore/b
Loading composer repositories with package information
Updating dependencies (including require-dev)
- Removing oreore/b (v1.0.0)
- Removing oreore/d (v1.0.0)
Writing lock file
Generating autoload files
.
.
.
どうせなら Bash の超ステキ機能 も使ってみましょうか。
export composer_uninstall='() {
composer remove --no-update "$@"
composer update --dry-run |
grep -Eo -e "- Uninstalling\s+\S+" |
cut -d" " -f3 |
xargs composer update
}'
exec $SHELL -l
次のように実行します。
$ composer_uninstall oreore/b
Loading composer repositories with package information
Updating dependencies (including require-dev)
- Removing oreore/b (v1.0.0)
- Removing oreore/d (v1.0.0)
Writing lock file
Generating autoload files
.
.
.
さいごのはネタです。