Repository: semantic-release/semantic-release (js)

Issue:
Plugins with array configuration not found when using `npx`
Hello!

## Current behavior

I'm using a shared configuration in an npm package that contains a plugin that is configured with the array form:
```
["@semantic-release/git", {
          "assets": ["package.json"],
          "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
      }]
```
And when I run everything with `npx`, it can't find the `@semantic-release/git` plugin:
```
[20:31:54] [semantic-release] › ✖  An error occurred while running semantic-release: { Error: Cannot find module '@semantic-release/git'
    at Function.Module._resolveFilename (module.js:547:15)
    at resolveFileName (/home/circleci/.npm/_npx/103/lib/node_modules/semantic-release/node_modules/resolve-from/index.js:29:39)
```

Note: the same happens with the `exec` plugin, I guess that there is something strange in the way the modules are resolved (also, this is working fine with 15.9).

## Expected behavior

Release is generated successfully

## Environment

- **semantic-release** version: 15.12.1
- CI environment: CircleCI
- Plugins used:
    "@semantic-release/commit-analyzer": "6.1.0",
    "@semantic-release/git": "7.0.5",
    "@semantic-release/github": "5.2.5",
    "@semantic-release/npm": "5.1.1",
    "@semantic-release/release-notes-generator": "7.1.4"

- **semantic-release** configuration:
```
module.exports = {
  "branch": "master",
  "plugins": [
      ["@semantic-release/commit-analyzer", {
          "releaseRules": [
              { "type": "ci", "release": "patch" }
          ]
      }],
      "@semantic-release/release-notes-generator",
      "@semantic-release/npm",
      "@semantic-release/github",
      ["@semantic-release/git", {
          "assets": ["package.json"],
          "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
      }]
  ],
  "preset": "angular"
};
```
- CI logs:
```
npx: installed 1477 in 22.866s
[20:31:54] [semantic-release] › ℹ  Running semantic-release version 15.12.1
[20:31:54] [semantic-release] › ✖  An error occurred while running semantic-release: { Error: Cannot find module '@semantic-release/git'
    at Function.Module._resolveFilename (module.js:547:15)
    at resolveFileName (/home/circleci/.npm/_npx/103/lib/node_modules/semantic-release/node_modules/resolve-from/index.js:29:39)
    at resolveFrom (/home/circleci/.npm/_npx/103/lib/node_modules/semantic-release/node_modules/resolve-from/index.js:43:9)
    at module.exports (/home/circleci/.npm/_npx/103/lib/node_modules/semantic-release/node_modules/resolve-from/index.js:46:41)
    at loadPlugin (/home/circleci/.npm/_npx/103/lib/node_modules/semantic-release/lib/plugins/utils.js:49:82)
    at options.plugins.castArray.reduce (/home/circleci/.npm/_npx/103/lib/node_modules/semantic-release/lib/plugins/index.js:17:37)
    at Array.reduce (<anonymous>)
    at module.exports (/home/circleci/.npm/_npx/103/lib/node_modules/semantic-release/lib/plugins/index.js:14:34)
    at module.exports (/home/circleci/.npm/_npx/103/lib/node_modules/semantic-release/lib/get-config.js:75:35)
    at <anonymous> code: 'MODULE_NOT_FOUND' }
{ Error: Cannot find module '@semantic-release/git'
    at Function.Module._resolveFilename (module.js:547:15)
    at resolveFileName (/home/circleci/.npm/_npx/103/lib/node_modules/semantic-release/node_modules/resolve-from/index.js:29:39)
    at resolveFrom (/home/circleci/.npm/_npx/103/lib/node_modules/semantic-release/node_modules/resolve-from/index.js:43:9)
    at module.exports (/home/circleci/.npm/_npx/103/lib/node_modules/semantic-release/node_modules/resolve-from/index.js:46:41)
    at loadPlugin (/home/circleci/.npm/_npx/103/lib/node_modules/semantic-release/lib/plugins/utils.js:49:82)
    at options.plugins.castArray.reduce (/home/circleci/.npm/_npx/103/lib/node_modules/semantic-release/lib/plugins/index.js:17:37)
    at Array.reduce (<anonymous>)
    at module.exports (/home/circleci/.npm/_npx/103/lib/node_modules/semantic-release/lib/plugins/index.js:14:34)
    at module.exports (/home/circleci/.npm/_npx/103/lib/node_modules/semantic-release/lib/get-config.js:75:35)
    at <anonymous> code: 'MODULE_NOT_FOUND' }Exited with code 1
```


Pull request:
fix: correctly resolve plugins installed globally with `npx`
This bug affects only plugins defined with the `plugins` option and wrapped in an Array to add a configuration

Fix #999

Patch:
diff --git a/lib/get-config.js b/lib/get-config.js
index b2c94b6..aaa76b7 100644
--- a/lib/get-config.js
+++ b/lib/get-config.js
@@ -6,6 +6,7 @@ const debug = require('debug')('semantic-release:config');
 const {repoUrl} = require('./git');
 const PLUGINS_DEFINITIONS = require('./definitions/plugins');
 const plugins = require('./plugins');
+const {validatePlugin, parseConfig} = require('./plugins/utils');
 
 const CONFIG_NAME = 'release';
 const CONFIG_FILES = [
@@ -37,17 +38,21 @@ module.exports = async (context, opts) => {
 
         // For each plugin defined in a shareable config, save in `pluginsPath` the extendable config path,
         // so those plugin will be loaded relatively to the config file
-        Object.entries(extendsOpts).reduce((pluginsPath, [option, value]) => {
-          if (PLUGINS_DEFINITIONS[option] || option === 'plugins') {
-            castArray(value)
-              .filter(plugin => isString(plugin) || (isPlainObject(plugin) && isString(plugin.path)))
-              .map(plugin => (isString(plugin) ? plugin : plugin.path))
-              .forEach(plugin => {
-                pluginsPath[plugin] = extendPath;
-              });
-          }
-          return pluginsPath;
-        }, pluginsPath);
+        Object.entries(extendsOpts)
+          .filter(([, value]) => Boolean(value))
+          .reduce((pluginsPath, [option, value]) => {
+            castArray(value).forEach(plugin => {
+              if (option === 'plugins' && validatePlugin(plugin)) {
+                pluginsPath[parseConfig(plugin)[0]] = extendPath;
+              } else if (
+                PLUGINS_DEFINITIONS[option] &&
+                (isString(plugin) || (isPlainObject(plugin) && isString(plugin.path)))
+              ) {
+                pluginsPath[isString(plugin) ? plugin : plugin.path] = extendPath;
+              }
+            });
+            return pluginsPath;
+          }, pluginsPath);
 
         return {...result, ...extendsOpts};
       }, {}),


Test patch:
diff --git a/test/get-config.test.js b/test/get-config.test.js
index 1cc9961..2fb13a7 100644
--- a/test/get-config.test.js
+++ b/test/get-config.test.js
@@ -212,7 +212,7 @@ test('Read configuration from file path in "extends"', async t => {
     branch: 'test_branch',
     repositoryUrl: 'https://host.null/owner/module.git',
     tagFormat: `v\${version}`,
-    plugins: false,
+    plugins: ['plugin-1', ['plugin-2', {plugin2Opt: 'value'}]],
   };
   // Create package.json and shareable.json in repository root
   await outputJson(path.resolve(cwd, 'package.json'), {release: pkgOptions});
@@ -227,6 +227,8 @@ test('Read configuration from file path in "extends"', async t => {
   t.deepEqual(t.context.plugins.args[0][1], {
     analyzeCommits: './shareable.json',
     generateNotes: './shareable.json',
+    'plugin-1': './shareable.json',
+    'plugin-2': './shareable.json',
   });
 });