Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(appium): prepare setup subcommand as shortcut for drivers/plugins installation #20102

Merged
merged 51 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
ffa447a
add help
KazuCocoa May 16, 2024
006742b
setup fail if something was already installed
KazuCocoa May 16, 2024
c56375c
adding install in setup
KazuCocoa May 16, 2024
af2502e
move into one js
KazuCocoa May 16, 2024
9d32e41
add all option
KazuCocoa May 16, 2024
1ed6f84
tweak description
KazuCocoa May 16, 2024
8f5223e
cleanup a bit and adding types
KazuCocoa May 16, 2024
f041b2c
tweaking types...
KazuCocoa May 16, 2024
68f7baf
add tests
KazuCocoa May 16, 2024
88cca05
tweak types more
KazuCocoa May 16, 2024
08e3c6d
new line
KazuCocoa May 16, 2024
be9d10d
Merge branch 'master' into subcommand-setup
KazuCocoa May 16, 2024
c29004b
modify the default command set
KazuCocoa May 17, 2024
a0b8701
tweak long line
KazuCocoa May 17, 2024
4fc664c
add reset to delete APPIUM_HOME
KazuCocoa May 17, 2024
b9207cc
add reset to delete APPIUM_HOME
KazuCocoa May 17, 2024
ae17a20
add uninstallation
KazuCocoa May 17, 2024
942b1f8
remove delete
KazuCocoa May 17, 2024
f9a2a0c
remove delete
KazuCocoa May 17, 2024
ca82886
add ?
KazuCocoa May 17, 2024
4435a5b
cleanup types
KazuCocoa May 17, 2024
869655b
tweak logs
KazuCocoa May 17, 2024
e426f09
tweak logs
KazuCocoa May 17, 2024
3f404ed
add images as default plugins
KazuCocoa May 17, 2024
05def96
Merge branch 'master' into subcommand-setup
KazuCocoa May 17, 2024
56bc740
rename a bit
KazuCocoa May 17, 2024
1ae4e3f
Merge branch 'master' into subcommand-setup
KazuCocoa May 17, 2024
8981f79
add '
KazuCocoa May 17, 2024
56119c5
add test
KazuCocoa May 18, 2024
4c1b00a
fix
KazuCocoa May 18, 2024
c32bff9
tweak
KazuCocoa May 19, 2024
bf41896
combins
KazuCocoa May 19, 2024
6f2bf92
simplify a bit
KazuCocoa May 19, 2024
59d59da
tweak the order
KazuCocoa May 19, 2024
b9e89cd
tweak type for installExtention
KazuCocoa May 19, 2024
afe4e90
build command for plugin
KazuCocoa May 19, 2024
3b32255
add driver command
KazuCocoa May 19, 2024
e7733dd
move try/catch
KazuCocoa May 19, 2024
a01675e
fix lint
KazuCocoa May 19, 2024
92b5d4e
tweak syntax
KazuCocoa May 19, 2024
985ccf5
define in one place
KazuCocoa May 19, 2024
063c757
remove blank lines
KazuCocoa May 19, 2024
d1126ba
extentionName
KazuCocoa May 19, 2024
91def85
extentionCommand
KazuCocoa May 19, 2024
e4d6d49
fix typo
KazuCocoa May 19, 2024
237217a
Merge branch 'master' into subcommand-setup
KazuCocoa May 19, 2024
ab7b153
remove uninstall and reset
KazuCocoa May 19, 2024
76a5d89
simplify a bit
KazuCocoa May 19, 2024
941e703
fix typo
KazuCocoa May 19, 2024
a3a9e6c
chore: reflect review, add description about subcommand to be used as…
KazuCocoa May 20, 2024
2223b33
Merge branch 'master' into subcommand-setup
KazuCocoa May 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 35 additions & 2 deletions packages/appium/lib/cli/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
EXT_SUBCOMMAND_UNINSTALL,
EXT_SUBCOMMAND_UPDATE,
PLUGIN_TYPE,
SERVER_SUBCOMMAND
SERVER_SUBCOMMAND,
SETUP_SUBCOMMAND
} from '../constants';
import {finalizeSchema, getArgSpec, hasArgSpec} from '../schema';
import {rootDir} from '../config';
Expand All @@ -24,7 +25,7 @@ export const EXTRA_ARGS = 'extraArgs';
* will automatially inject the `server` subcommand.
*/
const NON_SERVER_ARGS = Object.freeze(
new Set([DRIVER_TYPE, PLUGIN_TYPE, SERVER_SUBCOMMAND, '-h', '--help', '-v', '--version'])
new Set([SETUP_SUBCOMMAND, DRIVER_TYPE, PLUGIN_TYPE, SERVER_SUBCOMMAND, '-h', '--help', '-v', '--version'])
);

const version = fs.readPackageJsonFrom(rootDir).version;
Expand Down Expand Up @@ -76,6 +77,9 @@ class ArgParser {

const subParsers = parser.add_subparsers({dest: 'subcommand'});

// add the 'setup' command
ArgParser._addSetupToParser(subParsers);

// add the 'server' subcommand, and store the raw arguments on the parser
// object as a way for other parts of the code to work with the arguments
// conceptually rather than just through argparse
Expand Down Expand Up @@ -280,6 +284,35 @@ class ArgParser {
}
}
}

/**
*
* @param {import('argparse').SubParser} subParser
*/
static _addSetupToParser(subParser) {
KazuCocoa marked this conversation as resolved.
Show resolved Hide resolved
const setupParser = subParser.add_parser('setup', {
add_help: true,
help: 'Install latest uiautomator2 and xcuitest driver if no APPIUM_HOME was empty',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does if no APPIUM_HOME was empty mean? Do you mean to say unless APPIUM_HOME isn't empty, of if APPIUM_HOME is empty?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea, that was my bad. Right now is:

Install latest preset drivers and plugins if APPIUM_HOME has no drivers and plugins

});

ArgParser._patchExit(setupParser);
const extSubParsers = setupParser.add_subparsers({
dest: `setupCommand`,
});

const parserSpecs = [
{
command: 'all',
mykola-mokhnach marked this conversation as resolved.
Show resolved Hide resolved
help: `Install all available known drivers and plugins`,
aliases: ['a'],
},
];

for (const {command, help, aliases} of parserSpecs) {
const parser = extSubParsers.add_parser(command, {help, aliases: aliases ?? []});
ArgParser._patchExit(parser);
}
}
}

/**
Expand Down
71 changes: 71 additions & 0 deletions packages/appium/lib/cli/setup-command.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import _ from 'lodash';
import { KNOWN_DRIVERS, KNOWN_PLUGINS, SETUP_SUBCOMMAND } from '../constants';
import {runExtensionCommand} from './extension';

const DEFAULT_DRIVERS = ['uiautomator2', 'xcuitest'];
const DEFAULT_PLUGINS = ['images'];

const SUBCOMMAND_ALL = 'all';

export async function setupCommand(appiumHome, configArgs, driverConfig, pluginConfig) {
if (!_.isEmpty(driverConfig.installedExtensions) || !_.isEmpty(pluginConfig.installedExtensions)) {
mykola-mokhnach marked this conversation as resolved.
Show resolved Hide resolved
throw new Error(`'${SETUP_SUBCOMMAND}' will not run because '${appiumHome}' already has drivers: ${
_.isEmpty(driverConfig.installedExtensions)
? '(no drivers)'
: _.join(_.keys(driverConfig.installedExtensions), ',')
}, plugins: ${
_.isEmpty(pluginConfig.installedExtensions)
? '(no plugins)'
: _.join(_.keys(pluginConfig.installedExtensions), ',')
}`);
}

if (configArgs.setupCommand == SUBCOMMAND_ALL) {
await setupDriverFull(configArgs, driverConfig);
await setupPluginFull(configArgs, pluginConfig);
return;
}

await setupDriverDefault(configArgs, driverConfig);
await setupPluginDefault(configArgs, pluginConfig);
};

async function setupDriverDefault(configArgs, driverConfig) {
for (const driverName of DEFAULT_DRIVERS) {
await setupDriver(driverName, configArgs, driverConfig);
}
}

async function setupDriverFull(configArgs, driverConfig) {
for (const driver of _.keys(KNOWN_DRIVERS)) {
await setupDriver(driver, configArgs, driverConfig);
}
}

async function setupDriver(driverName, configArgs, driverConfig) {
const driverConfigArgs = configArgs;
driverConfigArgs.subcommand = 'driver';
driverConfigArgs.driverCommand = 'install';
driverConfigArgs.driver = driverName;
await runExtensionCommand(driverConfigArgs, driverConfig);
}

async function setupPluginDefault(configArgs, pluginConfig) {
for (const pluginName of DEFAULT_PLUGINS) {
await setupPlugin(pluginName, configArgs, pluginConfig);
}
}

async function setupPluginFull(configArgs, driverConfig) {
for (const pluginName of _.keys(KNOWN_PLUGINS)) {
await setupPlugin(pluginName, configArgs, driverConfig);
}
}

async function setupPlugin(pluginName, configArgs, pluginConfig) {
const pluginConfigArgs = configArgs;
pluginConfigArgs.subcommand = 'plugin';
pluginConfigArgs.pluginCommand = 'install';
pluginConfigArgs.plugin = pluginName;
await runExtensionCommand(pluginConfigArgs, pluginConfig);
}
6 changes: 6 additions & 0 deletions packages/appium/lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ export const PLUGIN_TYPE = 'plugin';
*/
export const SERVER_SUBCOMMAND = 'server';

/**
* The `setup` command of the `appium` CLI
*/
export const SETUP_SUBCOMMAND = 'setup'


/**
* The value of `--use-plugins` if _all_ plugins should be loaded
*/
Expand Down
9 changes: 7 additions & 2 deletions packages/appium/lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {asyncify} from 'asyncbox';
import _ from 'lodash';
import {AppiumDriver} from './appium';
import {runExtensionCommand} from './cli/extension';
import { setupCommand } from './cli/setup-command';
import {getParser} from './cli/parser';
import {
APPIUM_VER,
Expand All @@ -25,7 +26,7 @@ import {
} from './config';
import {readConfigFile} from './config-file';
import {loadExtensions, getActivePlugins, getActiveDrivers} from './extension';
import {SERVER_SUBCOMMAND, LONG_STACKTRACE_LIMIT} from './constants';
import {SERVER_SUBCOMMAND, LONG_STACKTRACE_LIMIT, SETUP_SUBCOMMAND} from './constants';
import registerNode from './grid-register';
import {getDefaultsForSchema, validate as validateSchema} from './schema/schema';
import {
Expand All @@ -38,6 +39,7 @@ import {
fetchInterfaces,
V4_BROADCAST_IP,
V6_BROADCAST_IP,
isSetupCommandArgs,
} from './utils';
import net from 'node:net';

Expand Down Expand Up @@ -216,7 +218,7 @@ async function init(args) {
}

// merge config and apply defaults.
// the order of precendece is:
// the order of precedence is:
// 1. command line args
// 2. config file
// 3. defaults from config file.
Expand Down Expand Up @@ -276,6 +278,9 @@ async function init(args) {
pluginConfig,
appiumHome,
});
} else if (isSetupCommandArgs(preConfigArgs)) {
await setupCommand(appiumHome, preConfigArgs, driverConfig, pluginConfig);
return /** @type {InitResult<Cmd>} */ ({});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe the return value should be the merge of the return values of all the install commands, in case this is run by script?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Meaning by appium setup? The current behavior is if the command succeeds, the existing status will be 0. Otherwise non-zero, so probably users can catch the error if an error occurred.

current e.g.

% ./node_modules/.bin/appium setup
dbug Appium No manifest file found at /Users/kazu/.appium/node_modules/.cache/appium/extensions.yaml; creating
dbug Appium Discovering newly installed extensions...
✔ Checking if 'appium-uiautomator2-driver' is compatible
✔ Installing 'uiautomator2' using NPM install spec 'appium-uiautomator2-driver'
ℹ Driver uiautomator2@3.5.1 successfully installed
- automationName: UiAutomator2
- platformNames: ["Android"]
✔ Checking if 'appium-xcuitest-driver' is compatible
✔ Installing 'xcuitest' using NPM install spec 'appium-xcuitest-driver'
ℹ Driver xcuitest@7.15.3 successfully installed
- automationName: XCUITest
- platformNames: ["iOS","tvOS"]
✔ Checking if '@appium/images-plugin' is compatible
✔ Installing 'images' using NPM install spec '@appium/images-plugin'
ℹ Plugin images@3.0.6 successfully installed
% echo $?                         
0

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i meant, in --json mode it can return a nice json object which is the merge of all the install operations.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-> #20130 I'll check and update the --json mode

} else {
await requireDir(appiumHome, true, appiumHomeSourceName);
if (isExtensionCommandArgs(preConfigArgs)) {
Expand Down
6 changes: 5 additions & 1 deletion packages/appium/lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {processCapabilities, PROTOCOLS, STANDARD_CAPS, errors} from '@appium/bas
import {inspect as dump} from 'util';
import {node, fs} from '@appium/support';
import path from 'path';
import {SERVER_SUBCOMMAND, DRIVER_TYPE, PLUGIN_TYPE} from './constants';
import {SERVER_SUBCOMMAND, DRIVER_TYPE, PLUGIN_TYPE, SETUP_SUBCOMMAND} from './constants';
import os from 'node:os';

const W3C_APPIUM_PREFIX = 'appium';
Expand Down Expand Up @@ -330,6 +330,10 @@ export function isServerCommandArgs(args) {
return args.subcommand === SERVER_SUBCOMMAND;
}

export function isSetupCommandArgs(args) {
return args.subcommand === SETUP_SUBCOMMAND;
}

/**
* @template {CliCommand} [Cmd=ServerCommand]
* @template {CliExtensionSubcommand|void} [SubCmd=void]
Expand Down
Loading