Skip to content

Commit

Permalink
chore: move winldd to CDN (#34078)
Browse files Browse the repository at this point in the history
  • Loading branch information
mxschmitt authored Dec 19, 2024
1 parent d7a5234 commit b7a1cfd
Show file tree
Hide file tree
Showing 11 changed files with 94 additions and 31 deletions.
Binary file removed packages/playwright-core/bin/PrintDeps.exe
Binary file not shown.
2 changes: 0 additions & 2 deletions packages/playwright-core/bin/README.md

This file was deleted.

5 changes: 5 additions & 0 deletions packages/playwright-core/browsers.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@
"mac12-arm64": "1010"
}
},
{
"name": "winldd",
"revision": "1007",
"installByDefault": false
},
{
"name": "android",
"revision": "1001",
Expand Down
3 changes: 3 additions & 0 deletions packages/playwright-core/src/cli/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ function checkBrowsersToInstall(args: string[], options: { noShell?: boolean, on
}
}

if (process.platform === 'win32')
executables.push(registry.findExecutable('winldd')!);

if (faultyArguments.length)
throw new Error(`Invalid installation targets: ${faultyArguments.map(name => `'${name}'`).join(', ')}. Expecting one of: ${suggestedBrowsersToInstall()}`);
return executables;
Expand Down
10 changes: 5 additions & 5 deletions packages/playwright-core/src/server/registry/dependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import childProcess from 'child_process';
import * as utils from '../../utils';
import { spawnAsync } from '../../utils/spawnAsync';
import { hostPlatform, isOfficiallySupportedPlatform } from '../../utils/hostPlatform';
import { buildPlaywrightCLICommand } from '.';
import { buildPlaywrightCLICommand, registry } from '.';
import { deps } from './nativeDeps';
import { getPlaywrightVersion } from '../../utils/userAgent';

Expand Down Expand Up @@ -122,12 +122,12 @@ export async function installDependenciesLinux(targets: Set<DependencyGroup>, dr
});
}

export async function validateDependenciesWindows(windowsExeAndDllDirectories: string[]) {
export async function validateDependenciesWindows(sdkLanguage: string, windowsExeAndDllDirectories: string[]) {
const directoryPaths = windowsExeAndDllDirectories;
const lddPaths: string[] = [];
for (const directoryPath of directoryPaths)
lddPaths.push(...(await executablesOrSharedLibraries(directoryPath)));
const allMissingDeps = await Promise.all(lddPaths.map(lddPath => missingFileDependenciesWindows(lddPath)));
const allMissingDeps = await Promise.all(lddPaths.map(lddPath => missingFileDependenciesWindows(sdkLanguage, lddPath)));
const missingDeps: Set<string> = new Set();
for (const deps of allMissingDeps) {
for (const dep of deps)
Expand Down Expand Up @@ -302,8 +302,8 @@ async function executablesOrSharedLibraries(directoryPath: string): Promise<stri
return executablersOrLibraries as string[];
}

async function missingFileDependenciesWindows(filePath: string): Promise<Array<string>> {
const executable = path.join(__dirname, '..', '..', '..', 'bin', 'PrintDeps.exe');
async function missingFileDependenciesWindows(sdkLanguage: string, filePath: string): Promise<Array<string>> {
const executable = registry.findExecutable('winldd')!.executablePathOrDie(sdkLanguage);
const dirname = path.dirname(filePath);
const { stdout, code } = await spawnAsync(executable, [filePath], {
cwd: dirname,
Expand Down
56 changes: 54 additions & 2 deletions packages/playwright-core/src/server/registry/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ const EXECUTABLE_PATHS = {
'mac': ['ffmpeg-mac'],
'win': ['ffmpeg-win64.exe'],
},
'winldd': {
'linux': undefined,
'mac': undefined,
'win': ['PrintDeps.exe'],
},
};

type DownloadPaths = Record<HostPlatform, string | undefined>;
Expand Down Expand Up @@ -315,6 +320,35 @@ const DOWNLOAD_PATHS: Record<BrowserName | InternalTool, DownloadPaths> = {
'mac15-arm64': 'builds/ffmpeg/%s/ffmpeg-mac-arm64.zip',
'win64': 'builds/ffmpeg/%s/ffmpeg-win64.zip',
},
'winldd': {
'<unknown>': undefined,
'ubuntu18.04-x64': undefined,
'ubuntu20.04-x64': undefined,
'ubuntu22.04-x64': undefined,
'ubuntu24.04-x64': undefined,
'ubuntu18.04-arm64': undefined,
'ubuntu20.04-arm64': undefined,
'ubuntu22.04-arm64': undefined,
'ubuntu24.04-arm64': undefined,
'debian11-x64': undefined,
'debian11-arm64': undefined,
'debian12-x64': undefined,
'debian12-arm64': undefined,
'mac10.13': undefined,
'mac10.14': undefined,
'mac10.15': undefined,
'mac11': undefined,
'mac11-arm64': undefined,
'mac12': undefined,
'mac12-arm64': undefined,
'mac13': undefined,
'mac13-arm64': undefined,
'mac14': undefined,
'mac14-arm64': undefined,
'mac15': undefined,
'mac15-arm64': undefined,
'win64': 'builds/winldd/%s/winldd-win64.zip',
},
'android': {
'<unknown>': 'builds/android/%s/android.zip',
'ubuntu18.04-x64': undefined,
Expand Down Expand Up @@ -442,7 +476,7 @@ function readDescriptors(browsersJSON: BrowsersJSON): BrowsersJSONDescriptor[] {
}

export type BrowserName = 'chromium' | 'firefox' | 'webkit' | 'bidi';
type InternalTool = 'ffmpeg' | 'firefox-beta' | 'chromium-tip-of-tree' | 'chromium-headless-shell' | 'chromium-tip-of-tree-headless-shell' | 'android';
type InternalTool = 'ffmpeg' | 'winldd' | 'firefox-beta' | 'chromium-tip-of-tree' | 'chromium-headless-shell' | 'chromium-tip-of-tree-headless-shell' | 'android';
type BidiChannel = 'bidi-firefox-stable' | 'bidi-firefox-beta' | 'bidi-firefox-nightly' | 'bidi-chrome-canary' | 'bidi-chrome-stable' | 'bidi-chromium';
type ChromiumChannel = 'chrome' | 'chrome-beta' | 'chrome-dev' | 'chrome-canary' | 'msedge' | 'msedge-beta' | 'msedge-dev' | 'msedge-canary';
const allDownloadable = ['android', 'chromium', 'firefox', 'webkit', 'ffmpeg', 'firefox-beta', 'chromium-tip-of-tree', 'chromium-headless-shell', 'chromium-tip-of-tree-headless-shell'];
Expand Down Expand Up @@ -772,6 +806,22 @@ export class Registry {
_dependencyGroup: 'tools',
_isHermeticInstallation: true,
});
const winldd = descriptors.find(d => d.name === 'winldd')!;
const winlddExecutable = findExecutablePath(winldd.dir, 'winldd');
this._executables.push({
type: 'tool',
name: 'winldd',
browserName: undefined,
directory: winldd.dir,
executablePath: () => winlddExecutable,
executablePathOrDie: (sdkLanguage: string) => executablePathOrDie('winldd', winlddExecutable, winldd.installByDefault, sdkLanguage),
installType: process.platform === 'win32' ? 'download-by-default' : 'none',
_validateHostRequirements: () => Promise.resolve(),
downloadURLs: this._downloadURLs(winldd),
_install: () => this._downloadExecutable(winldd, winlddExecutable),
_dependencyGroup: 'tools',
_isHermeticInstallation: true,
});
const android = descriptors.find(d => d.name === 'android')!;
this._executables.push({
type: 'tool',
Expand Down Expand Up @@ -944,7 +994,7 @@ export class Registry {
if (os.platform() === 'linux')
return await validateDependenciesLinux(sdkLanguage, linuxLddDirectories.map(d => path.join(browserDirectory, d)), dlOpenLibraries);
if (os.platform() === 'win32' && os.arch() === 'x64')
return await validateDependenciesWindows(windowsExeAndDllDirectories.map(d => path.join(browserDirectory, d)));
return await validateDependenciesWindows(sdkLanguage, windowsExeAndDllDirectories.map(d => path.join(browserDirectory, d)));
}

async installDeps(executablesToInstallDeps: Executable[], dryRun: boolean) {
Expand Down Expand Up @@ -1265,6 +1315,8 @@ export async function installBrowsersForNpmInstall(browsers: string[]) {
return false;
}
const executables: Executable[] = [];
if (process.platform === 'win32')
executables.push(registry.findExecutable('winldd')!);
for (const browserName of browsers) {
const executable = registry.findExecutable(browserName);
if (!executable || executable.installType === 'none')
Expand Down
8 changes: 4 additions & 4 deletions tests/installation/npmTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,22 @@ export const TMP_WORKSPACES = path.join(os.platform() === 'darwin' ? '/tmp' : os
const debug = debugLogger('itest');

const expect = _expect.extend({
toHaveLoggedSoftwareDownload(received: any, browsers: ('chromium' | 'chromium-headless-shell' | 'firefox' | 'webkit' | 'ffmpeg')[]) {
toHaveLoggedSoftwareDownload(received: string, browsers: ('chromium' | 'chromium-headless-shell' | 'firefox' | 'webkit' | 'winldd' |'ffmpeg')[]) {
if (typeof received !== 'string')
throw new Error(`Expected argument to be a string.`);

const downloaded = new Set();
let index = 0;
while (true) {
const match = received.substring(index).match(/(chromium|chromium headless shell|firefox|webkit|ffmpeg)[\s\d\.]+\(?playwright build v\d+\)? downloaded/im);
const match = received.substring(index).match(/(chromium|chromium headless shell|firefox|webkit|winldd|ffmpeg)[\s\d\.]+\(?playwright build v\d+\)? downloaded/im);
if (!match)
break;
downloaded.add(match[1].replace(/\s/g, '-').toLowerCase());
index += match.index + 1;
}

const expected = browsers;
if (expected.length === downloaded.size && expected.every(browser => downloaded.has(browser))) {
const expected = new Set(browsers);
if (expected.size === downloaded.size && [...expected].every(browser => downloaded.has(browser))) {
return {
pass: true,
message: () => 'Expected not to download browsers, but did.'
Expand Down
6 changes: 4 additions & 2 deletions tests/installation/playwright-cdn.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@ const parsedDownloads = (rawLogs: string) => {

test.use({ isolateBrowsers: true });

const extraInstalledSoftware = process.platform === 'win32' ? ['winldd' as const] : [];

for (const cdn of CDNS) {
test(`playwright cdn failover should work (${cdn})`, async ({ exec, checkInstalledSoftwareOnDisk }) => {
await exec('npm i playwright');
const result = await exec('npx playwright install', { env: { PW_TEST_CDN_THAT_SHOULD_WORK: cdn, DEBUG: 'pw:install' } });
expect(result).toHaveLoggedSoftwareDownload(['chromium', 'chromium-headless-shell', 'ffmpeg', 'firefox', 'webkit']);
await checkInstalledSoftwareOnDisk(['chromium', 'chromium-headless-shell', 'ffmpeg', 'firefox', 'webkit']);
expect(result).toHaveLoggedSoftwareDownload(['chromium', 'chromium-headless-shell', 'ffmpeg', 'firefox', 'webkit', ...extraInstalledSoftware]);
await checkInstalledSoftwareOnDisk((['chromium', 'chromium-headless-shell', 'ffmpeg', 'firefox', 'webkit', ...extraInstalledSoftware]));
const dls = parsedDownloads(result);
for (const software of ['chromium', 'ffmpeg', 'firefox', 'webkit'])
expect(dls).toContainEqual({ status: 200, name: software, url: expect.stringContaining(cdn) });
Expand Down
12 changes: 7 additions & 5 deletions tests/installation/playwright-cli-install-should-work.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,21 @@ import path from 'path';

test.use({ isolateBrowsers: true });

const extraInstalledSoftware = process.platform === 'win32' ? ['winldd' as const] : [];

test('install command should work', async ({ exec, checkInstalledSoftwareOnDisk }) => {
await exec('npm i playwright');

await test.step('playwright install chromium', async () => {
const result = await exec('npx playwright install chromium');
expect(result).toHaveLoggedSoftwareDownload(['chromium', 'chromium-headless-shell', 'ffmpeg']);
await checkInstalledSoftwareOnDisk(['chromium', 'chromium-headless-shell', 'ffmpeg']);
expect(result).toHaveLoggedSoftwareDownload(['chromium', 'chromium-headless-shell', 'ffmpeg', ...extraInstalledSoftware]);
await checkInstalledSoftwareOnDisk(['chromium', 'chromium-headless-shell', 'ffmpeg', ...extraInstalledSoftware]);
});

await test.step('playwright install', async () => {
const result = await exec('npx playwright install');
expect(result).toHaveLoggedSoftwareDownload(['firefox', 'webkit']);
await checkInstalledSoftwareOnDisk(['chromium', 'chromium-headless-shell', 'ffmpeg', 'firefox', 'webkit']);
await checkInstalledSoftwareOnDisk(['chromium', 'chromium-headless-shell', 'ffmpeg', 'firefox', 'webkit', ...extraInstalledSoftware]);
});

await exec('node sanity.js playwright', { env: { PLAYWRIGHT_BROWSERS_PATH: '0' } });
Expand All @@ -51,9 +53,9 @@ test('install command should work', async ({ exec, checkInstalledSoftwareOnDisk
test('should be able to remove browsers', async ({ exec, checkInstalledSoftwareOnDisk }) => {
await exec('npm i playwright');
await exec('npx playwright install chromium');
await checkInstalledSoftwareOnDisk(['chromium', 'chromium-headless-shell', 'ffmpeg']);
await checkInstalledSoftwareOnDisk(['chromium', 'chromium-headless-shell', 'ffmpeg', ...extraInstalledSoftware]);
await exec('npx playwright uninstall');
await checkInstalledSoftwareOnDisk([]);
await checkInstalledSoftwareOnDisk([...extraInstalledSoftware]);
});

test('should print the right install command without browsers', async ({ exec }) => {
Expand Down
22 changes: 12 additions & 10 deletions tests/installation/playwright-packages-install-behavior.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ import { test, expect } from './npmTest';

test.use({ isolateBrowsers: true });

const extraInstalledSoftware = process.platform === 'win32' ? ['winldd' as const] : [];

for (const browser of ['chromium', 'firefox', 'webkit']) {
test(`playwright-${browser} should work`, async ({ exec, checkInstalledSoftwareOnDisk }) => {
const pkg = `playwright-${browser}`;
const result = await exec('npm i --foreground-scripts', pkg);
const browserName = pkg.split('-')[1];
const expectedSoftware = [browserName];
const expectedSoftware = [browserName, ...extraInstalledSoftware];
if (browserName === 'chromium')
expectedSoftware.push('chromium-headless-shell', 'ffmpeg');
expect(result).toHaveLoggedSoftwareDownload(expectedSoftware as any);
Expand All @@ -37,7 +39,7 @@ for (const browser of ['chromium', 'firefox', 'webkit']) {
for (const browser of ['chromium', 'firefox', 'webkit']) {
test(`@playwright/browser-${browser} should work`, async ({ exec, checkInstalledSoftwareOnDisk }) => {
const pkg = `@playwright/browser-${browser}`;
const expectedSoftware = [browser];
const expectedSoftware = [browser, ...extraInstalledSoftware];
if (browser === 'chromium')
expectedSoftware.push('chromium-headless-shell', 'ffmpeg');

Expand Down Expand Up @@ -69,8 +71,8 @@ test(`playwright should work`, async ({ exec, checkInstalledSoftwareOnDisk }) =>
await checkInstalledSoftwareOnDisk([]);

const result2 = await exec('npx playwright install');
expect(result2).toHaveLoggedSoftwareDownload(['chromium', 'chromium-headless-shell', 'ffmpeg', 'firefox', 'webkit']);
await checkInstalledSoftwareOnDisk(['chromium', 'chromium-headless-shell', 'ffmpeg', 'firefox', 'webkit']);
expect(result2).toHaveLoggedSoftwareDownload(['chromium', 'chromium-headless-shell', 'ffmpeg', 'firefox', 'webkit', ...extraInstalledSoftware]);
await checkInstalledSoftwareOnDisk(['chromium', 'chromium-headless-shell', 'ffmpeg', 'firefox', 'webkit', ...extraInstalledSoftware]);

await exec('node sanity.js playwright chromium firefox webkit');
await exec('node esm-playwright.mjs');
Expand All @@ -81,17 +83,17 @@ test(`playwright should work with chromium --no-shell`, async ({ exec, checkInst
expect(result1).toHaveLoggedSoftwareDownload([]);
await checkInstalledSoftwareOnDisk([]);
const result2 = await exec('npx playwright install chromium --no-shell');
expect(result2).toHaveLoggedSoftwareDownload(['chromium', 'ffmpeg']);
await checkInstalledSoftwareOnDisk(['chromium', 'ffmpeg']);
expect(result2).toHaveLoggedSoftwareDownload(['chromium', 'ffmpeg', ...extraInstalledSoftware]);
await checkInstalledSoftwareOnDisk(['chromium', 'ffmpeg', ...extraInstalledSoftware]);
});

test(`playwright should work with chromium --only-shell`, async ({ exec, checkInstalledSoftwareOnDisk }) => {
const result1 = await exec('npm i --foreground-scripts playwright');
expect(result1).toHaveLoggedSoftwareDownload([]);
await checkInstalledSoftwareOnDisk([]);
const result2 = await exec('npx playwright install --only-shell');
expect(result2).toHaveLoggedSoftwareDownload(['chromium-headless-shell', 'ffmpeg', 'firefox', 'webkit']);
await checkInstalledSoftwareOnDisk(['chromium-headless-shell', 'ffmpeg', 'firefox', 'webkit']);
expect(result2).toHaveLoggedSoftwareDownload(['chromium-headless-shell', 'ffmpeg', 'firefox', 'webkit', ...extraInstalledSoftware]);
await checkInstalledSoftwareOnDisk(['chromium-headless-shell', 'ffmpeg', 'firefox', 'webkit', ...extraInstalledSoftware]);
});

test('@playwright/test should work', async ({ exec, checkInstalledSoftwareOnDisk }) => {
Expand All @@ -102,8 +104,8 @@ test('@playwright/test should work', async ({ exec, checkInstalledSoftwareOnDisk
await exec('npx playwright test -c . sample.spec.js', { expectToExitWithError: true, message: 'should not be able to run tests without installing browsers' });

const result2 = await exec('npx playwright install');
expect(result2).toHaveLoggedSoftwareDownload(['chromium', 'chromium-headless-shell', 'ffmpeg', 'firefox', 'webkit']);
await checkInstalledSoftwareOnDisk(['chromium', 'chromium-headless-shell', 'ffmpeg', 'firefox', 'webkit']);
expect(result2).toHaveLoggedSoftwareDownload(['chromium', 'chromium-headless-shell', 'ffmpeg', 'firefox', 'webkit', ...extraInstalledSoftware]);
await checkInstalledSoftwareOnDisk(['chromium', 'chromium-headless-shell', 'ffmpeg', 'firefox', 'webkit', ...extraInstalledSoftware]);

await exec('node sanity.js @playwright/test chromium firefox webkit');
await exec('node', 'esm-playwright-test.mjs');
Expand Down
1 change: 0 additions & 1 deletion tests/installation/playwright-test-plugin.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ function patchPackageJsonForPreReleaseIfNeeded(tmpWorkspace: string) {
// Workaround per https://stackoverflow.com/questions/71479750/npm-install-pre-release-versions-for-peer-dependency.
const pkg = JSON.parse(fs.readFileSync(path.resolve(tmpWorkspace, 'package.json'), 'utf-8'));
if (pkg.dependencies['@playwright/test'].match(/\d+\.\d+-\w+/)) {
console.log(`Setting overrides in package.json to make pre-release version of peer dependency work.`);
pkg.overrides = { '@playwright/test': '$@playwright/test' };
fs.writeFileSync(path.resolve(tmpWorkspace, 'package.json'), JSON.stringify(pkg, null, 2));
}
Expand Down

0 comments on commit b7a1cfd

Please sign in to comment.