UNPKG

6.75 kBJavaScriptView Raw
1import _ from 'lodash';
2import path from 'path';
3import { mkdirp, fs, system } from 'appium-support';
4import axios from 'axios';
5import { exec } from 'teen_process';
6import { rootDir } from './utils';
7import logger from './logger';
8import semver from 'semver';
9import {
10 StoreDeprecatedDefaultCapabilityAction, DEFAULT_CAPS_ARG,
11} from './argsparse-actions';
12
13
14const npmPackage = require(path.resolve(rootDir, 'package.json'));
15const APPIUM_VER = npmPackage.version;
16const MIN_NODE_VERSION = npmPackage.engines.node;
17
18const GIT_META_ROOT = '.git';
19const GIT_BINARY = `git${system.isWindows() ? '.exe' : ''}`;
20const GITHUB_API = 'https://api.github.com/repos/appium/appium';
21
22const BUILD_INFO = {
23 version: APPIUM_VER,
24};
25
26function getNodeVersion () {
27 return semver.coerce(process.version);
28}
29
30function isSubClass (candidateClass, superClass) {
31 return _.isFunction(superClass) && _.isFunction(candidateClass)
32 && (candidateClass.prototype instanceof superClass || candidateClass === superClass);
33}
34
35async function updateBuildInfo (useGithubApiFallback = false) {
36 const sha = await getGitRev(useGithubApiFallback);
37 if (!sha) {
38 return;
39 }
40 BUILD_INFO['git-sha'] = sha;
41 const built = await getGitTimestamp(sha, useGithubApiFallback);
42 if (!_.isEmpty(built)) {
43 BUILD_INFO.built = built;
44 }
45}
46
47async function getGitRev (useGithubApiFallback = false) {
48 if (await fs.exists(path.resolve(rootDir, GIT_META_ROOT))) {
49 try {
50 const {stdout} = await exec(GIT_BINARY, ['rev-parse', 'HEAD'], {
51 cwd: rootDir
52 });
53 return stdout.trim();
54 } catch (ign) {}
55 }
56
57 if (!useGithubApiFallback) {
58 return null;
59 }
60
61 try {
62 const resBodyObj = (await axios.get(`${GITHUB_API}/tags`, {
63 headers: {
64 'User-Agent': `Appium ${APPIUM_VER}`
65 }
66 })).data;
67 if (_.isArray(resBodyObj)) {
68 for (const {name, commit} of resBodyObj) {
69 if (name === `v${APPIUM_VER}` && commit && commit.sha) {
70 return commit.sha;
71 }
72 }
73 }
74 } catch (ign) {}
75 return null;
76}
77
78async function getGitTimestamp (commitSha, useGithubApiFallback = false) {
79 if (await fs.exists(path.resolve(rootDir, GIT_META_ROOT))) {
80 try {
81 const {stdout} = await exec(GIT_BINARY, ['show', '-s', '--format=%ci', commitSha], {
82 cwd: rootDir
83 });
84 return stdout.trim();
85 } catch (ign) {}
86 }
87
88 if (!useGithubApiFallback) {
89 return null;
90 }
91
92 try {
93 const resBodyObj = (await axios.get(`${GITHUB_API}/commits/${commitSha}`, {
94 headers: {
95 'User-Agent': `Appium ${APPIUM_VER}`
96 }
97 })).data;
98 if (resBodyObj && resBodyObj.commit) {
99 if (resBodyObj.commit.committer && resBodyObj.commit.committer.date) {
100 return resBodyObj.commit.committer.date;
101 }
102 if (resBodyObj.commit.author && resBodyObj.commit.author.date) {
103 return resBodyObj.commit.author.date;
104 }
105 }
106 } catch (ign) {}
107 return null;
108}
109
110/**
111 * @returns Mutable object containing Appium build information. By default it
112 * only contains the Appium version, but is updated with the build timestamp
113 * and git commit hash asynchronously as soon as `updateBuildInfo` is called
114 * and succeeds.
115 */
116function getBuildInfo () {
117 return BUILD_INFO;
118}
119
120function checkNodeOk () {
121 const version = getNodeVersion();
122 if (!semver.satisfies(version, MIN_NODE_VERSION)) {
123 logger.errorAndThrow(`Node version must be ${MIN_NODE_VERSION}. Currently ${version.version}`);
124 }
125}
126
127function warnNodeDeprecations () {
128 /**
129 * Uncomment this section to get node version deprecation warnings
130 * Also add test cases to config-specs.js to cover the cases added
131 **/
132
133 // const version = getNodeVersion();
134 // if (version.major < 8) {
135 // logger.warn(`Appium support for versions of node < ${version.major} has been ` +
136 // 'deprecated and will be removed in a future version. Please ' +
137 // 'upgrade!');
138 // }
139}
140
141async function showConfig () {
142 await updateBuildInfo(true);
143 console.log(JSON.stringify(getBuildInfo())); // eslint-disable-line no-console
144}
145
146function getNonDefaultArgs (parser, args) {
147 return parser.rawArgs.reduce((acc, [, {dest, default: defaultValue}]) => {
148 if (args[dest] && args[dest] !== defaultValue) {
149 acc[dest] = args[dest];
150 }
151 return acc;
152 }, {});
153}
154
155function getDeprecatedArgs (parser, args) {
156 // go through the server command line arguments and figure
157 // out which of the ones used are deprecated
158 return parser.rawArgs.reduce((acc, [[name], {dest, default: defaultValue, action}]) => {
159 if (!args[dest] || args[dest] === defaultValue) {
160 return acc;
161 }
162
163 if (action?.deprecated_for) {
164 acc[name] = action.deprecated_for;
165 } else if (isSubClass(action, StoreDeprecatedDefaultCapabilityAction)) {
166 acc[name] = DEFAULT_CAPS_ARG;
167 }
168 return acc;
169 }, {});
170}
171
172function checkValidPort (port, portName) {
173 if (port > 0 && port < 65536) return true; // eslint-disable-line curly
174 logger.error(`Port '${portName}' must be greater than 0 and less than 65536. Currently ${port}`);
175 return false;
176}
177
178function validateServerArgs (parser, args) {
179 // arguments that cannot both be set
180 let exclusives = [
181 ['noReset', 'fullReset'],
182 ['ipa', 'safari'],
183 ['app', 'safari'],
184 ['forceIphone', 'forceIpad'],
185 ['deviceName', 'defaultDevice']
186 ];
187
188 for (let exSet of exclusives) {
189 let numFoundInArgs = 0;
190 for (let opt of exSet) {
191 if (_.has(args, opt) && args[opt]) {
192 numFoundInArgs++;
193 }
194 }
195 if (numFoundInArgs > 1) {
196 throw new Error(`You can't pass in more than one argument from the ` +
197 `set ${JSON.stringify(exSet)}, since they are ` +
198 `mutually exclusive`);
199 }
200 }
201
202 const validations = {
203 port: checkValidPort,
204 callbackPort: checkValidPort,
205 bootstrapPort: checkValidPort,
206 chromedriverPort: checkValidPort,
207 robotPort: checkValidPort,
208 backendRetries: (r) => r >= 0,
209 };
210
211 const nonDefaultArgs = getNonDefaultArgs(parser, args);
212
213 for (let [arg, validator] of _.toPairs(validations)) {
214 if (_.has(nonDefaultArgs, arg)) {
215 if (!validator(args[arg], arg)) {
216 throw new Error(`Invalid argument for param ${arg}: ${args[arg]}`);
217 }
218 }
219 }
220}
221
222async function validateTmpDir (tmpDir) {
223 try {
224 await mkdirp(tmpDir);
225 } catch (e) {
226 throw new Error(`We could not ensure that the temp dir you specified ` +
227 `(${tmpDir}) exists. Please make sure it's writeable.`);
228 }
229}
230
231export {
232 getBuildInfo, validateServerArgs, checkNodeOk, showConfig,
233 warnNodeDeprecations, validateTmpDir, getNonDefaultArgs, getDeprecatedArgs,
234 getGitRev, checkValidPort, APPIUM_VER, updateBuildInfo,
235};