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

chore: move winldd to CDN #34078

Merged
merged 2 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
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')!);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we have this for winldd but not for ffmpeg?

Copy link
Member Author

@mxschmitt mxschmitt Dec 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is used for the browser-* packages which will install the browsers during the NPM install. We specify ffmpeg there every time on the caller side - we don't want to do that with winldd since its based on the process.platform so I moved it one layer deeper.

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
Loading