Repository: appium/appium (JavaScript)

Issue:
Appium2 cli can't handle long start server commands
Appium2 doesn't seem to be able to handle long server start commands. For both beta 40 and 41

command in question:

appium -p 11000 -pa /wd/hub --session-override  --local-timezone --relaxed-security --log-timestamp  -bp 11100 --default-capabilities '{"appium:systemPort": 11300, "appium:webviewDevtoolsPort": 11600, "appium:udid": "19271JEG502669", "appium:platformName": "ANDROID", "appium:platformVersion": "11", "appium:chomedriverPorts": [[11400, 11499]], "appium:deviceName": "19271JEG502669_Pixel_5a"}' --debug

Log: 
WARN Appium Appium encountered 1 warning while validating drivers found in manifest /local_home/exec/.appium/node_modules/.cache/appium/extensions.yaml
WARN Appium Driver "uiautomator2" has 1 potential problem: 
WARN Appium   - Driver "uiautomator2" (package `appium-uiautomator2-driver`) may be incompatible with the current version of Appium (v2.0.0-beta.41) due to an invalid or missing peer dependency on Appium. A newer version of `appium-uiautomator2-driver` is available; please attempt to upgrade "uiautomator2" to v2.4.6 or newer.

ENAMETOOLONG: name too long, open '{"appium:systemPort": 11300, "appium:webviewDevtoolsPort": 11600, "appium:udid": "19271JEG502669", "appium:platformName": "ANDROID", "appium:platformVersion": "11", "appium:chomedriverPorts": [[11400, 11499]], "appium:deviceName": "19271JEG502669_Pixel_5a"}'

Tested the exact command against 1.20.2 and it doesn't have any issues. 

Edit: For now I can probably use the relaxed-cap plugin to reduce the command length, but this seems like an arbitrary limitation and probably not intended. 



Discussion:
The way to do this with Appium 2 is to use a config file. It's a much cleaner solution. Here's an example config file: https://github.com/appium/appium/blob/master/packages/appium/sample-code/appium.config.sample.json
Ok I see, and I see now the nodeconfig file is also part of that config instead of a separate config file. But I'm assuming this limitation is not intentional?

actually i think this might be a bug @boneskull . maybe we first check to see if the value is a file that exists, which triggers this ENAMETOOLONG error, and then never get to the part where we parse it as JSON instead?
`nodeconfig` must be an object in the config file; this was intentional, but it may be undesirable.  as far as I recall, you can still pass a filepath on the command-line.  
> `nodeconfig` must be an object in the config file; this was intentional, but it may be undesirable. as far as I recall, you can still pass a filepath on the command-line.

This bug isn't related to the nodeconfig. The part of the command that's causing problem is the default capabilities
See #17352 for `ENAMETOOLONG` fix



Patch:
diff --git a/packages/appium/lib/schema/cli-transformers.js b/packages/appium/lib/schema/cli-transformers.js
index 297b06357c5..591d5e6cdfd 100644
--- a/packages/appium/lib/schema/cli-transformers.js
+++ b/packages/appium/lib/schema/cli-transformers.js
@@ -94,8 +94,9 @@ export const transformers = {
       json = readFileSync(jsonOrPath, 'utf8');
       loadedFromFile = true;
     } catch (err) {
-      // unreadable files don't count... but other problems do.
-      if (err.code !== 'ENOENT') {
+      // unreadable files don't count.
+      // also `ENAMETOOLONG` can happen if we try to open a file that's a huge JSON string.
+      if (err.code !== 'ENOENT' && err.code !== 'ENAMETOOLONG') {
         throw err;
       }
     }


Test patch:
diff --git a/packages/appium/test/e2e/cli.e2e.spec.js b/packages/appium/test/e2e/cli.e2e.spec.js
index 3d6f163b83d..880d5cbeae3 100644
--- a/packages/appium/test/e2e/cli.e2e.spec.js
+++ b/packages/appium/test/e2e/cli.e2e.spec.js
@@ -1,5 +1,7 @@
 // @ts-check
 
+import getPort from 'get-port';
+import {exec} from 'teen_process';
 import {npm, env, fs, tempDir, util} from '@appium/support';
 import B from 'bluebird';
 import path from 'path';
@@ -15,13 +17,14 @@ import {
   KNOWN_DRIVERS,
   PLUGIN_TYPE,
 } from '../../lib/constants';
-import {FAKE_DRIVER_DIR, resolveFixture} from '../helpers';
+import {FAKE_DRIVER_DIR, PACKAGE_ROOT, resolveFixture} from '../helpers';
 import {
   installLocalExtension,
   runAppiumJson,
   runAppiumRaw,
   readAppiumArgErrorFixture,
   formatAppiumArgErrorOutput,
+  EXECUTABLE,
 } from './e2e-helpers';
 
 const {MANIFEST_RELATIVE_PATH} = env;
@@ -523,7 +526,41 @@ describe('CLI behavior', function () {
     });
   });
 
-  describe('argument error handling', function () {
+  describe('argument parsing', function () {
+    describe('when the user provides a very long string for an arg accepting a blob or filename', function () {
+      describe('when the very long string is a JSON blob', function () {
+        it('should not throw an ENAMETOOLONG exception', async function () {
+          this.timeout(10000);
+          const capsArg = JSON.stringify({
+            'appium:platformName': 'ANDROID',
+            'appium:platformVersion': '11',
+            'appium:deviceName':
+              'Spicy jalapeno bacon ipsum dolor amet deserunt tempor pork belly aliqua drumstick, occaecat dolor venison et labore. Rump meatball pork chop tail. Consequat adipisicing kielbasa occaecat laborum pig. Qui pork chop chicken nostrud boudin fugiat. Proident ut culpa, chuck nulla sunt pastrami ut tri-tip. Buffalo dolore adipisicing, labore venison elit beef fatback kevin burgdoggen tail pancetta filet mignon. Dolor turducken rump, anim kevin sunt exercitation ham filet mignon beef ribs ad officia eiusmod id cillum.',
+          });
+
+          await expect(
+            exec(
+              process.execPath,
+              [
+                EXECUTABLE,
+                '-pa=/wd/hub',
+                '--session-override',
+                '--local-timezone',
+                '--relaxed-security',
+                `--default-capabilities=${capsArg}`,
+                '--port',
+                await getPort(),
+              ],
+              {
+                env: {APPIUM_HOME: appiumHome, PATH: process.env.PATH},
+                cwd: PACKAGE_ROOT,
+                timeout: 5000,
+              }
+            )
+          ).to.be.rejectedWith(Error, /timed out/);
+        });
+      });
+    });
     describe('when the user provides an string where a number was expected', function () {
       describe('when color output is supported', function () {
         it('should output a fancy error message', async function () {