古いcordova-cliでplugin addが失敗したときのトラブルシューティング

# 概要

~~cordovaのバージョンをアップグレードしたら負けかなと思った~~ 

最近古いバージョンのcordova開発環境を再現させる必要があったけど、plugin add周りで思いの外色んな問題に遭遇したのでメモとして書き残します。

# 5系のトラブルシューティング

自分の環境：

- node: 6.11.3
- npm: 3.10.10
- cordova-cli: 5.4.1
- cordova-ios: 3.9.2
- cordova-android: 4.1.1

## `Path must be a string. Received undefined`

`plugin rm`のときに遭遇するエラーです：

```
Uninstalling ***** from ios
Beginning processing of action stack for ios project...
Error during processing of action! Attempting to revert...
Error: TypeError: Uh oh!
Path must be a string. Received undefined
    at assertPath (path.js:7:11)
    at Object.dirname (path.js:1331:5)
```

これが一度出ていると、もう一度`plugin rm`するときは違うエラーに変わることがあります：

```
Installing Android library: com.google.android.gms:play-services-gcm:7.0.0
Error: Uh oh!
ENOENT: no such file or directory, scandir '***'
    at Error (native)
    at Object.fs.readdirSync (fs.js:953:18)
```

### 解決法

解決法はいずれも、一度`platform rm`してから`plugin rm`するだけです：

```
cordova platform rm ios
cordova platform rm android
cordova plugin rm <問題が発生したプラグイン>
cordova platform add ios
cordova platform add android
```

# 4系のトラブルシューティング

自分の環境：

- node: 4.8.6
- npm: 2.15.11
- cordova-cli: 4.3.1
- cordova-ios: 3.8.0
- cordova-android: 3.7.2

## `plugin add`がタイムアウトする

例えば、下記のように`plugin add`が終わらなくなることが発生します：

```
cordova plugin add cordova-plugin-console
Fetching plugin "cordova-plugin-console" via plugin registry
npm http GET http://registry.cordova.io/cordova-plugin-console
```

その理由は、cordova-cli 5.0からパッケージ管理の移行があったからです[^1]。

[^1]: http://cordova.apache.org/announcements/2015/04/21/plugins-release-and-move-to-npm.html

### 解決法

解決法は、直接url指定で`plugin add`します。たとえば上記のプラグインだと、下記のようにインストールできます：

```
cordova plugin add https://github.com/apache/cordova-plugin-console.git#1.0.0
```

## `ENOENT: no such file or directory, open '*****/platforms/android/local.properties'`

`plugin add`でこのエラーが出る場合があります：

```
Installing "*****" for android
    Can't find module read-package-json, running npm install
Error during processing of action! Attempting to revert...
Failed to install '*****':Error: Uh oh!
ENOENT: no such file or directory, open '*****/platforms/android/local.properties'
    at Error (native)
    at Object.fs.openSync (fs.js:549:18)
    at Object.fs.readFileSync (fs.js:397:15)
    ...
```


一度`cordova build android`すれば`./platform/android/local.properties`が生成されますが、また`plugin add`しようとすると、今度は別のエラーが出ます[^2]：

[^2]: どうやら、r25だとこれが発生していて、r26では発生しません。しかし、r26では`cordova build android`のときにエラーが出るので、結局r25に切り替える必要があります。


```
Installing "*****" for android
    Node module read-package-json is found
    Module read-package-json is installed
Error during processing of action! Attempting to revert...
Failed to install '*****':TypeError: Uh oh!
Path must be a string. Received undefined
    at assertPath (path.js:8:11)
    at Object.posix.resolve (path.js:426:5)
    ...
```

一見、上記の5系のエラーと同じものに見えますが、こちらは`platform rm`しても解決しません。

`-d`つけて追ってみると、どうやら`./platform/android/local.properties`にsdk dirが設定されていないのが原因みたいです。下記のようにパスを設定しておけば、`plugin add`は無事突破します：

```
sdk.dir=<ANDROID SDKのパス>
```

ただ、この状態だと`cordova build android`で次のようなエラーが出ることがあります。

## `Error: ENOENT: no such file or directory, open '/.../r25.2.5/com.google.android.gms:play-services-gcm:7.0.0/build.xml'`

これの原因を辿ると、比較的に新しいandroid用の`framework`記述が原因だと分かります。

### 解決法

いろいろ試行錯誤した結果、まず最近のライブラリーを使おうとするとantには無理があるので、`ANDROIB_BUILD=gradle`をセットしてgradleを使います。それから、`plugin.xml`に記述された依存は、下記のように手動で`platform/android/build-extras.gradle`に記述すれば、`cordova build android`がやっと通ります：

```groovy
dependencies {
    compile 'com.google.android.gms:play-services-gcm:7.0.0'
}
```