UNPKG

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