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: support npx playwright install msedge #6861

Merged
merged 7 commits into from
Jun 7, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
27 changes: 26 additions & 1 deletion .github/workflows/tests_secondary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,31 @@ jobs:
name: firefox-stable-mac-test-results
path: test-results

edge_stable_mac:
name: "Edge Stable (Mac)"
runs-on: macos-10.15
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 12
- run: npm ci
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: npm run build
- run: node lib/cli/cli install msedge
- run: npm run ctest
env:
PWTEST_CHANNEL: msedge
- run: ./utils/upload_flakiness_dashboard.sh ./test-results/report.json
if: always()
- uses: actions/upload-artifact@v1
if: ${{ always() }}
with:
name: msedge-stable-mac-test-results
path: test-results


edge_stable_win:
name: "Edge Stable (Win)"
runs-on: windows-latest
Expand All @@ -345,7 +370,7 @@ jobs:
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: npm run build
- run: node lib/cli/cli install ffmpeg
- run: node lib/cli/cli install msedge
- run: npm run ctest
shell: bash
env:
Expand Down
1 change: 1 addition & 0 deletions bin/reinstall_chrome_beta_linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ wget https://dl.google.com/linux/direct/google-chrome-beta_current_amd64.deb
sudo apt-get install -y ./google-chrome-beta_current_amd64.deb
rm -rf ./google-chrome-beta_current_amd64.deb
cd -
google-chrome-beta --version
3 changes: 3 additions & 0 deletions bin/reinstall_chrome_beta_mac.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ curl -o ./googlechromebeta.dmg -k https://dl.google.com/chrome/mac/beta/googlech
hdiutil attach -nobrowse -quiet -noautofsck -noautoopen -mountpoint /Volumes/googlechromebeta.dmg ./googlechromebeta.dmg
cp -rf "/Volumes/googlechromebeta.dmg/Google Chrome Beta.app" /Applications
hdiutil detach /Volumes/googlechromebeta.dmg
rm -rf /tmp/googlechromebeta.dmg

/Applications/Google\ Chrome\ Beta.app/Contents/MacOS/Google\ Chrome\ Beta --version
21 changes: 13 additions & 8 deletions bin/reinstall_chrome_beta_win.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,22 @@ if ([Environment]::Is64BitProcess) {
$url = 'https://dl.google.com/tag/s/dl/chrome/install/beta/googlechromebetastandaloneenterprise64.msi'
}

$app = Get-WmiObject -Class Win32_Product | Where-Object {
$_.Name -match "Google Chrome Beta"
}
if ($app) {
$app.Uninstall()
}

Write-Host "Downloading Google Chrome Beta"
$wc = New-Object net.webclient
$msiInstaller = "$env:temp\google-chrome-beta.msi"
Remove-Item $msiInstaller
$wc.Downloadfile($url, $msiInstaller)

Write-Host "Installing Google Chrome Beta"
$arguments = "/i `"$msiInstaller`" /quiet"
Start-Process msiexec.exe -ArgumentList $arguments -Wait
Remove-Item $msiInstaller

$suffix = "\\Google\\Chrome Beta\\Application\\chrome.exe"
if (Test-Path "${env:ProgramFiles(x86)}$suffix") {
(Get-Item "${env:ProgramFiles(x86)}$suffix").VersionInfo
} elseif (Test-Path "${env:ProgramFiles}$suffix") {
(Get-Item "${env:ProgramFiles}$suffix").VersionInfo
} else {
write-host "ERROR: failed to install Google Chrome Beta"
exit 1
}
1 change: 1 addition & 0 deletions bin/reinstall_chrome_stable_linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo apt-get install -y ./google-chrome-stable_current_amd64.deb
rm -rf ./google-chrome-stable_current_amd64.deb
cd -
google-chrome --version
4 changes: 3 additions & 1 deletion bin/reinstall_chrome_stable_mac.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ set -x

rm -rf "/Applications/Google Chrome.app"
cd /tmp
curl -o ./googlechrome.dmg -k https://dl.google.com/chrome/mac/beta/googlechrome.dmg
curl -o ./googlechrome.dmg -k https://dl.google.com/chrome/mac/googlechrome.dmg
hdiutil attach -nobrowse -quiet -noautofsck -noautoopen -mountpoint /Volumes/googlechrome.dmg ./googlechrome.dmg
cp -rf "/Volumes/googlechrome.dmg/Google Chrome.app" /Applications
hdiutil detach /Volumes/googlechrome.dmg
rm -rf /tmp/googlechrome.dmg
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --version
26 changes: 16 additions & 10 deletions bin/reinstall_chrome_stable_win.ps1
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
$url = 'https://dl.google.com/tag/s/dl/chrome/install/beta/googlechromestandaloneenterprise.msi';
$url = 'https://dl.google.com/tag/s/dl/chrome/install/googlechromestandaloneenterprise.msi';

if ([Environment]::Is64BitProcess) {
$url = 'https://dl.google.com/tag/s/dl/chrome/install/beta/googlechromestandaloneenterprise64.msi'
}

$app = Get-WmiObject -Class Win32_Product | Where-Object {
$_.Name -eq "Google Chrome"
}
if ($app) {
$app.Uninstall()
$url = 'https://dl.google.com/tag/s/dl/chrome/install/googlechromestandaloneenterprise64.msi'
}

$wc = New-Object net.webclient
$msiInstaller = "$env:temp\google-chrome.msi"
Remove-Item $msiInstaller
Write-Host "Downloading Google Chrome"
$wc.Downloadfile($url, $msiInstaller)

Write-Host "Installing Google Chrome"
$arguments = "/i `"$msiInstaller`" /quiet"
Start-Process msiexec.exe -ArgumentList $arguments -Wait
Remove-Item $msiInstaller


$suffix = "\\Google\\Chrome\\Application\\chrome.exe"
if (Test-Path "${env:ProgramFiles(x86)}$suffix") {
(Get-Item "${env:ProgramFiles(x86)}$suffix").VersionInfo
} elseif (Test-Path "${env:ProgramFiles}$suffix") {
(Get-Item "${env:ProgramFiles}$suffix").VersionInfo
} else {
write-host "ERROR: failed to install Google Chrome"
exit 1
}
11 changes: 11 additions & 0 deletions bin/reinstall_msedge_stable_mac.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash
set -e
set -x

cd /tmp
curl -o ./msedge_stable.pkg -k "$1"
# Note: there's no way to uninstall previously installed MSEdge.
# However, running PKG again seems to update installation.
sudo installer -pkg /tmp/msedge_stable.pkg -target /
yury-s marked this conversation as resolved.
Show resolved Hide resolved
rm -rf /tmp/msedge_stable.pkg
/Applications/Microsoft\ Edge.app/Contents/MacOS/Microsoft\ Edge --version
21 changes: 21 additions & 0 deletions bin/reinstall_msedge_stable_win.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
$url = $args[0]

Write-Host "Downloading Microsoft Edge"
$wc = New-Object net.webclient
$msiInstaller = "$env:temp\microsoft-edge-stable.msi"
$wc.Downloadfile($url, $msiInstaller)

Write-Host "Installing Microsoft Edge"
$arguments = "/i `"$msiInstaller`" /quiet"
Start-Process msiexec.exe -ArgumentList $arguments -Wait
Remove-Item $msiInstaller

$suffix = "\\Microsoft\\Edge\\Application\\msedge.exe"
if (Test-Path "${env:ProgramFiles(x86)}$suffix") {
(Get-Item "${env:ProgramFiles(x86)}$suffix").VersionInfo
} elseif (Test-Path "${env:ProgramFiles}$suffix") {
(Get-Item "${env:ProgramFiles}$suffix").VersionInfo
} else {
write-host "ERROR: failed to install Microsoft Edge"
exit 1
}
88 changes: 55 additions & 33 deletions src/cli/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,31 @@ import * as utils from '../utils/utils';

const SCRIPTS_DIRECTORY = path.join(__dirname, '..', '..', 'bin');

type BrowserChannel = 'chrome-beta'|'chrome';
const allBrowserChannels: Set<BrowserChannel> = new Set(['chrome-beta', 'chrome']);
type BrowserChannel = 'chrome-beta'|'chrome'|'msedge';
const allBrowserChannels: Set<BrowserChannel> = new Set(['chrome-beta', 'chrome', 'msedge']);

const ChannelName = {
'chrome-beta': 'Google Chrome Beta',
'chrome': 'Google Chrome',
'msedge': 'Microsoft Edge',
};

const InstallationScriptName = {
'chrome-beta': {
'linux': 'reinstall_chrome_beta_linux.sh',
'darwin': 'reinstall_chrome_beta_mac.sh',
'win32': 'reinstall_chrome_beta_win.ps1',
},
'chrome': {
'linux': 'reinstall_chrome_stable_linux.sh',
'darwin': 'reinstall_chrome_stable_mac.sh',
'win32': 'reinstall_chrome_stable_win.ps1',
},
'msedge': {
'darwin': 'reinstall_msedge_stable_mac.sh',
'win32': 'reinstall_msedge_stable_win.ps1',
},
};

program
.version('Version ' + require('../../package.json').version)
Expand Down Expand Up @@ -107,47 +130,46 @@ program
console.log(`Invalid installation targets: ${faultyArguments.map(name => `'${name}'`).join(', ')}. Expecting one of: ${[...allBrowserNames, ...allBrowserChannels].map(name => `'${name}'`).join(', ')}`);
process.exit(1);
}
if (browserNames.has('chromium') || browserChannels.has('chrome-beta') || browserChannels.has('chrome'))
if (browserNames.has('chromium') || browserChannels.has('chrome-beta') || browserChannels.has('chrome') || browserChannels.has('msedge'))
browserNames.add('ffmpeg');
if (browserNames.size)
await installBrowsers([...browserNames]);
for (const browserChannel of browserChannels) {
if (browserChannel === 'chrome-beta' || browserChannel === 'chrome')
await installChromeChannel(browserChannel);
else
throw new Error(`ERROR: no installation instructions for '${browserChannel}' channel.`);
}
for (const browserChannel of browserChannels)
await installBrowserChannel(browserChannel);
} catch (e) {
console.log(`Failed to install browsers\n${e}`);
process.exit(1);
}
});

async function installChromeChannel(channel: string) {
const platform: string = os.platform();
const shell: (string|undefined) = {
'linux': 'bash',
'darwin': 'bash',
'win32': 'powershell.exe',
}[platform];
const scriptName: (string|undefined) = ({
'chrome-beta': {
'linux': 'reinstall_chrome_beta_linux.sh',
'darwin': 'reinstall_chrome_beta_mac.sh',
'win32': 'reinstall_chrome_beta_win.ps1',
},
'chrome': {
'linux': 'reinstall_chrome_stable_linux.sh',
'darwin': 'reinstall_chrome_stable_mac.sh',
'win32': 'reinstall_chrome_stable_win.ps1',
},
}[channel] as any)[platform];
if (!shell || !scriptName)
throw new Error(`Cannot install chrome-beta on ${platform}`);

const {code} = await utils.spawnAsync(shell, [path.join(SCRIPTS_DIRECTORY, scriptName)], { cwd: SCRIPTS_DIRECTORY, stdio: 'inherit' });
async function installBrowserChannel(channel: BrowserChannel) {
const platform = os.platform();
const scriptName: (string|undefined) = (InstallationScriptName[channel] as any)[platform];
if (!scriptName)
throw new Error(`Cannot install ${ChannelName[channel]} on ${platform}`);

const scriptArgs = [];
if (channel === 'msedge') {
const products = JSON.parse(await utils.fetchData('https://edgeupdates.microsoft.com/api/products'));
const stable = products.find((product: any) => product.Product === 'Stable');
if (platform === 'win32') {
const arch = os.arch() === 'x64' ? 'x64' : 'x86';
const release = stable.Releases.find((release: any) => release.Platform === 'Windows' && release.Architecture === arch);
const artifact = release.Artifacts.find((artifact: any) => artifact.ArtifactName === 'msi');
scriptArgs.push(artifact.Location /* url */);
} else if (platform === 'darwin') {
const release = stable.Releases.find((release: any) => release.Platform === 'MacOS' && release.Architecture === 'universal');
const artifact = release.Artifacts.find((artifact: any) => artifact.ArtifactName === 'pkg');
scriptArgs.push(artifact.Location /* url */);
} else {
throw new Error(`Cannot install ${ChannelName[channel]} on ${platform}`);
}
}

const shell = scriptName.endsWith('.ps1') ? 'powershell.exe' : 'bash';
const {code} = await utils.spawnAsync(shell, [path.join(SCRIPTS_DIRECTORY, scriptName), ...scriptArgs], { cwd: SCRIPTS_DIRECTORY, stdio: 'inherit' });
if (code !== 0)
throw new Error('Failed to install chrome-beta');
throw new Error(`Failed to install ${ChannelName[channel]}`);
}

program
Expand Down
89 changes: 2 additions & 87 deletions src/install/browserFetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,10 @@ import fs from 'fs';
import os from 'os';
import path from 'path';
import ProgressBar from 'progress';
import { getProxyForUrl } from 'proxy-from-env';
import * as URL from 'url';
import { BrowserName, Registry, hostPlatform } from '../utils/registry';
import { downloadFile, existsAsync } from '../utils/utils';
import { debugLogger } from '../utils/debugLogger';

// `https-proxy-agent` v5 is written in TypeScript and exposes generated types.
// However, as of June 2020, its types are generated with tsconfig that enables
// `esModuleInterop` option.
//
// As a result, we can't depend on the package unless we enable the option
// for our codebase. Instead of doing this, we abuse "require" to import module
// without types.
const ProxyAgent = require('https-proxy-agent');

const existsAsync = (path: string): Promise<boolean> => new Promise(resolve => fs.stat(path, err => resolve(!err)));

export type OnProgressCallback = (downloadedBytes: number, totalBytes: number) => void;

export async function downloadBrowserWithProgressBar(registry: Registry, browserName: BrowserName): Promise<boolean> {
const browserDirectory = registry.browserDirectory(browserName);
const progressBarName = `${browserName} v${registry.revision(browserName)}`;
Expand Down Expand Up @@ -71,7 +57,7 @@ export async function downloadBrowserWithProgressBar(registry: Registry, browser
try {
for (let attempt = 1, N = 3; attempt <= N; ++attempt) {
debugLogger.log('install', `downloading ${progressBarName} - attempt #${attempt}`);
const {error} = await downloadFile(url, zipPath, progress);
const {error} = await downloadFile(url, zipPath, {progressCallback: progress, log: debugLogger.log.bind(debugLogger, 'install')});
if (!error) {
debugLogger.log('install', `SUCCESS downloading ${progressBarName}`);
break;
Expand Down Expand Up @@ -111,77 +97,6 @@ function toMegabytes(bytes: number) {
return `${Math.round(mb * 10) / 10} Mb`;
}

function downloadFile(url: string, destinationPath: string, progressCallback: OnProgressCallback | undefined): Promise<{error: any}> {
debugLogger.log('install', `running download:`);
debugLogger.log('install', `-- from url: ${url}`);
debugLogger.log('install', `-- to location: ${destinationPath}`);
let fulfill: ({error}: {error: any}) => void = ({error}) => {};
let downloadedBytes = 0;
let totalBytes = 0;

const promise: Promise<{error: any}> = new Promise(x => { fulfill = x; });

const request = httpRequest(url, 'GET', response => {
if (response.statusCode !== 200) {
const error = new Error(`Download failed: server returned code ${response.statusCode}. URL: ${url}`);
// consume response data to free up memory
response.resume();
fulfill({error});
return;
}
const file = fs.createWriteStream(destinationPath);
file.on('finish', () => fulfill({error: null}));
file.on('error', error => fulfill({error}));
response.pipe(file);
totalBytes = parseInt(response.headers['content-length'], 10);
debugLogger.log('install', `-- total bytes: ${totalBytes}`);
if (progressCallback)
response.on('data', onData);
});
request.on('error', (error: any) => fulfill({error}));
return promise;

function onData(chunk: string) {
downloadedBytes += chunk.length;
progressCallback!(downloadedBytes, totalBytes);
}
}

function httpRequest(url: string, method: string, response: (r: any) => void) {
let options: any = URL.parse(url);
options.method = method;

const proxyURL = getProxyForUrl(url);
if (proxyURL) {
if (url.startsWith('http:')) {
const proxy = URL.parse(proxyURL);
options = {
path: options.href,
host: proxy.hostname,
port: proxy.port,
};
} else {
const parsedProxyURL: any = URL.parse(proxyURL);
parsedProxyURL.secureProxy = parsedProxyURL.protocol === 'https:';

options.agent = new ProxyAgent(parsedProxyURL);
options.rejectUnauthorized = false;
}
}

const requestCallback = (res: any) => {
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location)
httpRequest(res.headers.location, method, response);
else
response(res);
};
const request = options.protocol === 'https:' ?
require('https').request(options, requestCallback) :
require('http').request(options, requestCallback);
request.end();
return request;
}

export function logPolitely(toBeLogged: string) {
const logLevel = process.env.npm_config_loglevel;
const logLevelDisplay = ['silent', 'error', 'warn'].indexOf(logLevel || '') > -1;
Expand Down
Loading