forked from stoneman/appium-windows-driver
-
Notifications
You must be signed in to change notification settings - Fork 37
Expand file tree
/
Copy pathinstaller.ts
More file actions
116 lines (110 loc) · 4.23 KB
/
installer.ts
File metadata and controls
116 lines (110 loc) · 4.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import _ from 'lodash';
import { fs, tempDir } from 'appium/support';
import path from 'node:path';
import { exec } from 'teen_process';
import { log } from './logger';
import { queryRegistry, type RegEntry } from './registry';
import { runElevated } from './utils';
const POSSIBLE_WAD_INSTALL_ROOTS = [
process.env['ProgramFiles(x86)'],
process.env.ProgramFiles,
`${process.env.SystemDrive || 'C:'}\\\\Program Files`,
];
const WAD_EXE_NAME = 'WinAppDriver.exe';
const UNINSTALL_REG_ROOT = 'HKLM\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall';
const REG_ENTRY_VALUE = 'Windows Application Driver';
const REG_ENTRY_KEY = 'DisplayName';
const REG_ENTRY_TYPE = 'REG_SZ';
const INST_LOCATION_SCRIPT_BY_GUID = (guid: string): string => `
Set installer = CreateObject("WindowsInstaller.Installer")
Set session = installer.OpenProduct("${guid}")
session.DoAction("CostInitialize")
session.DoAction("CostFinalize")
WScript.Echo session.Property("INSTALLFOLDER")
`.replace(/\n/g, '\r\n');
/**
* Fetches the MSI installation location for a given installer GUID
*
* @param installerGuid - The MSI installer GUID
* @returns The installation location path
*/
async function fetchMsiInstallLocation(installerGuid: string): Promise<string> {
const tmpRoot = await tempDir.openDir();
const scriptPath = path.join(tmpRoot, 'get_wad_inst_location.vbs');
try {
await fs.writeFile(scriptPath, INST_LOCATION_SCRIPT_BY_GUID(installerGuid), 'latin1');
const {stdout} = await runElevated('cscript.exe', ['/Nologo', scriptPath]);
return _.trim(stdout);
} finally {
await fs.rimraf(tmpRoot);
}
}
class WADNotFoundError extends Error {}
export const getWADExecutablePath = _.memoize(async function getWADInstallPath(): Promise<string> {
const wadPath = process.env.APPIUM_WAD_PATH ?? '';
if (await fs.exists(wadPath)) {
log.debug(`Loaded WinAppDriver path from the APPIUM_WAD_PATH environment variable: ${wadPath}`);
return wadPath;
}
// TODO: WAD installer should write the full path to it into the system registry
const pathCandidates = POSSIBLE_WAD_INSTALL_ROOTS
// remove unset env variables
.filter((root): root is string => Boolean(root))
// construct full path
.map((root) => path.resolve(root, REG_ENTRY_VALUE, WAD_EXE_NAME));
for (const result of pathCandidates) {
if (await fs.exists(result)) {
return result;
}
}
log.debug('Did not detect WAD executable at any of the default install locations');
log.debug('Checking the system registry for the corresponding MSI entry');
try {
const uninstallEntries = await queryRegistry(UNINSTALL_REG_ROOT);
const wadEntry = uninstallEntries.find((entry: RegEntry) =>
entry.key === REG_ENTRY_KEY && entry.value === REG_ENTRY_VALUE && entry.type === REG_ENTRY_TYPE
);
if (wadEntry) {
log.debug(`Found MSI entry: ${JSON.stringify(wadEntry)}`);
const installerGuid = _.last(wadEntry.root.split('\\')) as string;
// WAD MSI installer leaves InstallLocation registry value empty,
// so we need to be hacky here
const result = path.join(
await fetchMsiInstallLocation(installerGuid),
WAD_EXE_NAME
);
log.debug(`Checking if WAD exists at '${result}'`);
if (await fs.exists(result)) {
return result;
}
log.debug(result);
} else {
log.debug('No WAD MSI entries have been found');
}
} catch (e: any) {
if (e.stderr) {
log.debug(e.stderr);
}
log.debug(e.stack);
}
throw new WADNotFoundError(`${WAD_EXE_NAME} has not been found in any of these ` +
`locations: ${pathCandidates}. Use the following driver script to install it: ` +
`'appium driver run windows install-wad <optional_wad_version>'. ` +
`Check https://github.com/microsoft/WinAppDriver/releases to list ` +
`available server versions or drop the '<optional_wad_version>' argument to ` +
`install the latest stable one.`
);
});
/**
* Checks if the current process is running with administrator privileges
*
* @returns Promise that resolves to true if running as admin, false otherwise
*/
export async function isAdmin(): Promise<boolean> {
try {
await exec('fsutil.exe', ['dirty', 'query', process.env.SystemDrive || 'C:']);
return true;
} catch {
return false;
}
}