1 | import _ from 'lodash';
|
2 | import path from 'path';
|
3 | import { mkdirp, fs, system, util } from 'appium-support';
|
4 | import request from 'request-promise';
|
5 | import { exec } from 'teen_process';
|
6 | import { rootDir } from './utils';
|
7 | import logger from './logger';
|
8 | import semver from 'semver';
|
9 |
|
10 |
|
11 | const npmPackage = require(path.resolve(rootDir, 'package.json'));
|
12 | const APPIUM_VER = npmPackage.version;
|
13 | const MIN_NODE_VERSION = npmPackage.engines.node;
|
14 |
|
15 | const GIT_META_ROOT = '.git';
|
16 | const GIT_BINARY = `git${system.isWindows() ? '.exe' : ''}`;
|
17 | const GITHUB_API = 'https://api.github.com/repos/appium/appium';
|
18 |
|
19 | const BUILD_INFO = {
|
20 | version: APPIUM_VER,
|
21 | };
|
22 |
|
23 | function getNodeVersion () {
|
24 | return semver.coerce(process.version);
|
25 | }
|
26 |
|
27 | async 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 |
|
39 | async 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 |
|
71 | async 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 |
|
106 |
|
107 |
|
108 |
|
109 |
|
110 | function getBuildInfo () {
|
111 | return BUILD_INFO;
|
112 | }
|
113 |
|
114 | function 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 |
|
121 | function warnNodeDeprecations () {
|
122 | |
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 |
|
131 |
|
132 |
|
133 | }
|
134 |
|
135 | async function showConfig () {
|
136 | await updateBuildInfo(true);
|
137 | console.log(JSON.stringify(getBuildInfo()));
|
138 | }
|
139 |
|
140 | function 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 |
|
151 | function getDeprecatedArgs (parser, args) {
|
152 |
|
153 |
|
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 |
|
166 | function checkValidPort (port, portName) {
|
167 | if (port > 0 && port < 65536) return true;
|
168 | logger.error(`Port '${portName}' must be greater than 0 and less than 65536. Currently ${port}`);
|
169 | return false;
|
170 | }
|
171 |
|
172 | function validateServerArgs (parser, args) {
|
173 |
|
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 |
|
216 | async 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 |
|
225 | export {
|
226 | getBuildInfo, validateServerArgs, checkNodeOk, showConfig,
|
227 | warnNodeDeprecations, validateTmpDir, getNonDefaultArgs, getDeprecatedArgs,
|
228 | getGitRev, checkValidPort, APPIUM_VER, updateBuildInfo,
|
229 | };
|