chore: drop dependency on playwright-core in all our packages (#2318)

This patch:
- drops dependency on playwright-core in all our packages. Instead of
  the dependency, packages are now built with `//packages/build_package.sh`
  script.
- unifies `browsers.json` - now there's a single `//browsers.json` file
  that is used to manage browser revisions.

This patch temporary switches canary publishing to `--dryn-run` from CI/CD so that we
can verify that it does sane things.

We'll unify all our package management scripts under `//packages/` in a
follow-up.

Fixes #2268
This commit is contained in:
Andrey Lushnikov 2020-05-21 13:18:15 -07:00 committed by GitHub
parent 2ede4bce12
commit 505d94ab1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 291 additions and 149 deletions

1
.gitignore vendored
View File

@ -16,5 +16,4 @@ yarn.lock
/src/firefox/protocol.ts
/src/webkit/protocol.ts
lib/
playwright-*.tgz
/types/*

View File

@ -1,3 +1,6 @@
# NOTE: .npmignore is copied over to every subpackage in //packages when NPM packages
# are built.
#
# this ignores everything by default, except for package.json and LICENSE and README.md
# see https://docs.npmjs.com/misc/developers
**/*
@ -10,8 +13,11 @@ lib/injected/
!types/*
!index.d.ts
# root for "playwright-core" package
!index.js
!install.js
!README.md
!LICENSE
!NOTICE
# browser descriptor
!browsers.json

View File

@ -18,7 +18,6 @@ const { Playwright } = require('./lib/server/playwright');
const { Electron } = require('./lib/server/electron');
const path = require('path');
const playwrightRoot = path.join(__dirname, 'packages', 'playwright');
const playwright = new Playwright(playwrightRoot, require(path.join(playwrightRoot, 'browsers.json'))['browsers']);
const playwright = new Playwright(__dirname, require(path.join(__dirname, 'browsers.json'))['browsers']);
playwright.electron = new Electron();
module.exports = playwright;

View File

@ -64,10 +64,9 @@ async function downloadAllBrowsersAndGenerateProtocolTypes() {
const { installBrowsersWithProgressBar } = require('./lib/install/installer');
const protocolGenerator = require('./utils/protocol-types-generator');
const browserPaths = require('./lib/install/browserPaths');
const playwrightRoot = path.join(__dirname, 'packages', 'playwright');
const browsersPath = browserPaths.browsersPath(playwrightRoot);
const browsers = require(path.join(playwrightRoot, 'browsers.json'))['browsers'];
await installBrowsersWithProgressBar(playwrightRoot);
const browsersPath = browserPaths.browsersPath(__dirname);
const browsers = require(path.join(__dirname, 'browsers.json'))['browsers'];
await installBrowsersWithProgressBar(__dirname);
for (const browser of browsers) {
const browserPath = browserPaths.browserDirectory(browsersPath, browser);
await protocolGenerator.generateProtocol(browser.name, browserPaths.executablePath(browserPath, browser)).catch(console.warn);

2
package-lock.json generated
View File

@ -1,5 +1,5 @@
{
"name": "playwright-core",
"name": "playwright-internal",
"version": "1.0.0-post",
"lockfileVersion": 1,
"requires": true,

View File

@ -1,5 +1,6 @@
{
"name": "playwright-core",
"name": "playwright-internal",
"private": true,
"version": "1.0.0-post",
"description": "A high-level API to automate web browsers",
"repository": "github:Microsoft/playwright",

47
packages/README.md Normal file
View File

@ -0,0 +1,47 @@
# Managing and Publishing Playwright Packages
## Overview
- Playwright ships multiple packages to NPM. All packges that are published to NPM are listed as folders under [`//packages/`](../packages).
- Playwright's [root package.json](../package.json) is **never published to NPM**. It is only used for devmode, e.g. when running `npm install` with no arguments or installing from github.
- Playwright dependencies for all packages are the same and are managed with the [`root package.json`](../package.json).
- Playwright browser versions for all packages are the same and are managed with the [`browsers.json`](../browsers.json).
> **NOTE** As of May 20, 2020, the only exception is the `playwright-electron` package that
> doesn't follow the pack and is published manually. This is due to it's pre-1.0 status.
## Building NPM package
To build a package that will be shipped to NPM, use [`//packages/build_package.js`](./build_package.js) script.
The script populates package folder with contents, and then uses `npm pack` to archive the folder.
As of May 20, 2020, [`//packages/build_package.js`](./build_package.js) does the following:
- copies certain files and folders from `playwright-internal` to the subpackage (e.g. `//lib`, `//types`, `//LICENSE` etc)
- generates `package.json` and puts it in the subpackage
- generates `browsers.json` and puts it in the subpackage
- uses `npm pack` to pack the subpackage folder
- removes all the files that were added during the process
To build `playwright` package and save result as `./playwright.tgz` file:
```sh
$ ./packages/build_package.js playwright ./playwright.tgz
```
To debug what files are put into the folder, use `--no-cleanup` flag and inspect the package folder:
```sh
$ ./packages/build_package.js playwright ./playwright.tgz --no-cleanup
$ ls ./packages/playwright # inspect the folder
```
## Testing packages
To test packages, use [`//tests/installation-tests/installation-tests.sh`](../tests/installation-tests/installation-tests.sh).
## Publishing packages
All package publishing happens **exclusively** over CI/CD using the [`//utils/publish_all_packages.sh`](../utils/publish_all_packages.sh) script.

174
packages/build_package.js Executable file
View File

@ -0,0 +1,174 @@
#!/usr/bin/env node
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const fs = require('fs');
const path = require('path');
const rmSync = require('rimraf').sync;
const ncp = require('ncp');
const {spawnSync} = require('child_process');
const util = require('util');
const writeFileAsync = util.promisify(fs.writeFile.bind(fs));
const cpAsync = util.promisify(ncp);
const SCRIPT_NAME = path.basename(__filename);
const ROOT_PATH = path.join(__dirname, '..');
const PACKAGE_FILES = ['lib', 'types', 'NOTICE', 'LICENSE', '.npmignore'];
const PACKAGES = {
'playwright': {
description: 'A high-level API to automate web browsers',
whitelistedBrowsers: ['chromium', 'firefox', 'webkit'],
// We copy README.md additionally for Playwright so that it looks nice on NPM.
files: [...PACKAGE_FILES, 'README.md'],
},
'playwright-core': {
description: 'A high-level API to automate web browsers',
whitelistedBrowsers: [],
files: PACKAGE_FILES,
},
'playwright-webkit': {
description: 'A high-level API to automate WebKit',
whitelistedBrowsers: ['webkit'],
files: PACKAGE_FILES,
},
'playwright-firefox': {
description: 'A high-level API to automate Firefox',
whitelistedBrowsers: ['firefox'],
files: PACKAGE_FILES,
},
'playwright-chromium': {
description: 'A high-level API to automate Chromium',
whitelistedBrowsers: ['chromium'],
files: PACKAGE_FILES,
},
};
const cleanupPaths = [];
// 1. Parse CLI arguments
const args = process.argv.slice(2);
if (args.some(arg => arg === '--help')) {
console.log(usage());
process.exit(1);
} else if (args.length < 1) {
console.log(`Please specify package name, e.g. 'playwright' or 'playwright-chromium'.`);
console.log(`Try running ${SCRIPT_NAME} --help`);
process.exit(1);
} else if (args.length < 2) {
console.log(`Please specify output path`);
console.log(`Try running ${SCRIPT_NAME} --help`);
process.exit(1);
}
// 2. Setup cleanup if needed
if (!args.some(arg => arg === '--no-cleanup')) {
process.on('exit', () => {
cleanupPaths.forEach(cleanupPath => rmSync(cleanupPath, {}));
});
process.on('SIGINT', () => process.exit(2));
process.on('SIGHUP', () => process.exit(3));
process.on('SIGTERM', () => process.exit(4));
process.on('uncaughtException', error => {
console.error(error);
process.exit(5);
});
process.on('unhandledRejection', error => {
console.error(error);
process.exit(6);
});
}
const packageName = args[0];
const outputPath = path.resolve(args[1]);
const packagePath = path.join(__dirname, packageName);
const package = PACKAGES[packageName];
if (!package) {
console.log(`ERROR: unknown package ${packageName}`);
process.exit(1);
}
(async () => {
// 3. Copy package files.
for (const file of package.files)
await copyToPackage(file);
// 4. Generate package.json
const packageJSON = require(path.join(ROOT_PATH, 'package.json'));
await writeToPackage('package.json', JSON.stringify({
name: packageName,
version: packageJSON.version,
description: package.description,
repository: packageJSON.repository,
engines: packageJSON.engines,
homepage: packageJSON.homepage,
main: 'index.js',
scripts: {
install: 'node install.js',
},
author: packageJSON.author,
license: packageJSON.license,
dependencies: packageJSON.dependencies
}, null, 2));
// 5. Generate browsers.json
const browsersJSON = require(path.join(ROOT_PATH, 'browsers.json'));
browsersJSON.browsers = browsersJSON.browsers.filter(browser => package.whitelistedBrowsers.includes(browser.name));
await writeToPackage('browsers.json', JSON.stringify(browsersJSON, null, 2));
// 6. Run npm pack
const {stdout, stderr, status} = spawnSync('npm', ['pack'], {cwd: packagePath, encoding: 'utf8'});
if (status !== 0) {
console.log(`ERROR: "npm pack" failed`);
console.log(stderr);
process.exit(1);
}
const tgzName = stdout.trim();
// 7. Move result to the outputPath
fs.renameSync(path.join(packagePath, tgzName), outputPath);
console.log(outputPath);
})();
async function writeToPackage(fileName, content) {
const toPath = path.join(packagePath, fileName);
cleanupPaths.push(toPath);
console.error(`- generating: //${path.relative(ROOT_PATH, toPath)}`);
await writeFileAsync(toPath, content);
}
async function copyToPackage(fileOrDirectoryName) {
const fromPath = path.join(ROOT_PATH, fileOrDirectoryName);
const toPath = path.join(packagePath, fileOrDirectoryName);
cleanupPaths.push(toPath);
console.error(`- copying: //${path.relative(ROOT_PATH, fromPath)} -> //${path.relative(ROOT_PATH, toPath)}`);
await cpAsync(fromPath, toPath);
}
function usage() {
return `
usage: ${SCRIPT_NAME} <package-name> <output-path> [--no-cleanup]
Creates a .tgz of the package and saves it at the given output path
--no-cleanup skip cleaning up generated files from package directory
Example:
${SCRIPT_NAME} playwright ./playwright.tgz
`;
}

View File

@ -1,8 +0,0 @@
{
"browsers": [
{
"name": "chromium",
"revision": "767256"
}
]
}

View File

@ -14,6 +14,6 @@
* limitations under the License.
*/
const { Playwright } = require('playwright-core/lib/server/playwright');
const { Playwright } = require('./lib/server/playwright');
module.exports = new Playwright(__dirname, require('./browsers.json')['browsers']);

View File

@ -14,6 +14,6 @@
* limitations under the License.
*/
const { installBrowsersWithProgressBar } = require('playwright-core/lib/install/installer');
const { installBrowsersWithProgressBar } = require('./lib/install/installer');
installBrowsersWithProgressBar(__dirname);

View File

@ -1,18 +0,0 @@
{
"name": "playwright-chromium",
"version": "1.0.0-post",
"description": "A high-level API to automate Chromium",
"repository": "github:Microsoft/playwright",
"homepage": "https://playwright.dev",
"main": "index.js",
"scripts": {
"install": "node install.js"
},
"author": {
"name": "Microsoft Corporation"
},
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "=1.0.0-post"
}
}

View File

@ -0,0 +1,3 @@
# playwright-core
This package contains the no-browser flavor of [Playwright](http://github.com/microsoft/playwright).

View File

@ -0,0 +1,17 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* NOTE: playwright-core does not install browsers by design. */

View File

@ -1,8 +0,0 @@
{
"browsers": [
{
"name": "firefox",
"revision": "1094"
}
]
}

View File

@ -14,6 +14,6 @@
* limitations under the License.
*/
const { Playwright } = require('playwright-core/lib/server/playwright');
const { Playwright } = require('./lib/server/playwright');
module.exports = new Playwright(__dirname, require('./browsers.json')['browsers']);

View File

@ -14,6 +14,6 @@
* limitations under the License.
*/
const { installBrowsersWithProgressBar } = require('playwright-core/lib/install/installer');
const { installBrowsersWithProgressBar } = require('./lib/install/installer');
installBrowsersWithProgressBar(__dirname);

View File

@ -1,18 +0,0 @@
{
"name": "playwright-firefox",
"version": "1.0.0-post",
"description": "A high-level API to automate Firefox",
"repository": "github:Microsoft/playwright",
"homepage": "https://playwright.dev",
"main": "index.js",
"scripts": {
"install": "node install.js"
},
"author": {
"name": "Microsoft Corporation"
},
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "=1.0.0-post"
}
}

View File

@ -1,8 +0,0 @@
{
"browsers": [
{
"name": "webkit",
"revision": "1241"
}
]
}

View File

@ -14,6 +14,6 @@
* limitations under the License.
*/
const { Playwright } = require('playwright-core/lib/server/playwright');
const { Playwright } = require('./lib/server/playwright');
module.exports = new Playwright(__dirname, require('./browsers.json')['browsers']);

View File

@ -14,6 +14,6 @@
* limitations under the License.
*/
const { installBrowsersWithProgressBar } = require('playwright-core/lib/install/installer');
const { installBrowsersWithProgressBar } = require('./lib/install/installer');
installBrowsersWithProgressBar(__dirname);

View File

@ -1,18 +0,0 @@
{
"name": "playwright-webkit",
"version": "1.0.0-post",
"description": "A high-level API to automate WebKit",
"repository": "github:Microsoft/playwright",
"homepage": "https://playwright.dev",
"main": "index.js",
"scripts": {
"install": "node install.js"
},
"author": {
"name": "Microsoft Corporation"
},
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "=1.0.0-post"
}
}

View File

@ -1,2 +0,0 @@
# copied from the root readme
README.md

View File

@ -1 +0,0 @@
# empty to not exclude things in gitignore

View File

@ -14,6 +14,6 @@
* limitations under the License.
*/
const { Playwright } = require('playwright-core/lib/server/playwright');
const { Playwright } = require('./lib/server/playwright');
module.exports = new Playwright(__dirname, require('./browsers.json')['browsers']);

View File

@ -14,6 +14,6 @@
* limitations under the License.
*/
const { installBrowsersWithProgressBar } = require('playwright-core/lib/install/installer');
const { installBrowsersWithProgressBar } = require('./lib/install/installer');
installBrowsersWithProgressBar(__dirname);

View File

@ -1,19 +0,0 @@
{
"name": "playwright",
"version": "1.0.0-post",
"description": "A high-level API to automate web browsers",
"repository": "github:Microsoft/playwright",
"homepage": "https://playwright.dev",
"main": "index.js",
"scripts": {
"install": "node install.js",
"prepublishOnly": "cp ../../README.md ."
},
"author": {
"name": "Microsoft Corporation"
},
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "=1.0.0-post"
}
}

View File

@ -5,31 +5,22 @@ set +x
trap "cd $(pwd -P)" EXIT
cd "$(dirname $0)"
# 1. Pack all packages.
rm -rf ./output
mkdir ./output
cd ./output
npm pack ../../..
npm pack ../../../packages/playwright
npm pack ../../../packages/playwright-chromium
npm pack ../../../packages/playwright-webkit
npm pack ../../../packages/playwright-firefox
# cleanup environment
unset PLAYWRIGHT_DOWNLOAD_HOST
unset PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD
export PLAYWRIGHT_BROWSERS_PATH=0
# There is no option to specify output for `npm pack`, but the format is
# fixed.
PACKAGE_VERSION=$(node -e 'console.log(require("../../../package.json").version)')
PLAYWRIGHT_CORE_TGZ="$(pwd -P)/playwright-core-${PACKAGE_VERSION}.tgz"
PLAYWRIGHT_TGZ="$(pwd -P)/playwright-${PACKAGE_VERSION}.tgz"
PLAYWRIGHT_CHROMIUM_TGZ="$(pwd -P)/playwright-chromium-${PACKAGE_VERSION}.tgz"
PLAYWRIGHT_WEBKIT_TGZ="$(pwd -P)/playwright-webkit-${PACKAGE_VERSION}.tgz"
PLAYWRIGHT_FIREFOX_TGZ="$(pwd -P)/playwright-firefox-${PACKAGE_VERSION}.tgz"
# Pack all packages and put them in our output folder.
PACKAGE_BUILDER="../../../packages/build_package.js"
PLAYWRIGHT_CORE_TGZ="$(node ${PACKAGE_BUILDER} playwright-core ./playwright-core.tgz)"
PLAYWRIGHT_TGZ="$(node ${PACKAGE_BUILDER} playwright ./playwright.tgz)"
PLAYWRIGHT_CHROMIUM_TGZ="$(node ${PACKAGE_BUILDER} playwright-chromium ./playwright-chromium.tgz)"
PLAYWRIGHT_WEBKIT_TGZ="$(node ${PACKAGE_BUILDER} playwright-webkit ./playwright-webkit.tgz)"
PLAYWRIGHT_FIREFOX_TGZ="$(node ${PACKAGE_BUILDER} playwright-firefox ./playwright-firefox.tgz)"
SANITY_JS="$(pwd -P)/../sanity.js"
TEST_ROOT="$(pwd -P)"

View File

@ -2,7 +2,16 @@
set -e
set -x
trap "cd $(pwd -P)" EXIT
function cleanup {
# Cleanup all possibly created package tars.
[[ ! -z "${PLAYWRIGHT_TGZ}" ]] && rm -rf "${PLAYWRIGHT_TGZ}"
[[ ! -z "${PLAYWRIGHT_CORE_TGZ}" ]] && rm -rf "${PLAYWRIGHT_CORE_TGZ}"
[[ ! -z "${PLAYWRIGHT_WEBKIT_TGZ}" ]] && rm -rf "${PLAYWRIGHT_WEBKIT_TGZ}"
[[ ! -z "${PLAYWRIGHT_FIREFOX_TGZ}" ]] && rm -rf "${PLAYWRIGHT_FIREFOX_TGZ}"
[[ ! -z "${PLAYWRIGHT_CHROMIUM_TGZ}" ]] && rm -rf "${PLAYWRIGHT_CHROMIUM_TGZ}"
}
trap "cleanup; cd $(pwd -P)" EXIT
cd "$(dirname $0)"
if [[ $1 == "--help" ]]; then
@ -66,11 +75,16 @@ else
exit 1
fi
npm run clean
npm publish . --tag="${NPM_PUBLISH_TAG}"
npm publish packages/playwright-firefox --tag="${NPM_PUBLISH_TAG}"
npm publish packages/playwright-webkit --tag="${NPM_PUBLISH_TAG}"
npm publish packages/playwright-chromium --tag="${NPM_PUBLISH_TAG}"
npm publish packages/playwright --tag="${NPM_PUBLISH_TAG}"
PLAYWRIGHT_TGZ="$(node ./packages/build_package.js playwright ./playwright.tgz)"
PLAYWRIGHT_CORE_TGZ="$(node ./packages/build_package.js playwright-core ./playwright-core.tgz)"
PLAYWRIGHT_WEBKIT_TGZ="$(node ./packages/build_package.js playwright-webkit ./playwright-webkit.tgz)"
PLAYWRIGHT_FIREFOX_TGZ="$(node ./packages/build_package.js playwright-firefox ./playwright-firefox.tgz)"
PLAYWRIGHT_CHROMIUM_TGZ="$(node ./packages/build_package.js playwright-chromium ./playwright-chromium.tgz)"
npm publish ${PLAYWRIGHT_TGZ} --tag="${NPM_PUBLISH_TAG}" --dry-run
npm publish ${PLAYWRIGHT_CORE_TGZ} --tag="${NPM_PUBLISH_TAG}" --dry-run
npm publish ${PLAYWRIGHT_WEBKIT_TGZ} --tag="${NPM_PUBLISH_TAG}" --dry-run
npm publish ${PLAYWRIGHT_FIREFOX_TGZ} --tag="${NPM_PUBLISH_TAG}" --dry-run
npm publish ${PLAYWRIGHT_CHROMIUM_TGZ} --tag="${NPM_PUBLISH_TAG}" --dry-run
echo "Done."

View File

@ -59,18 +59,10 @@ if (version === '--next') {
version = version.substring(1);
}
updatePackage(path.join(__dirname, '..', 'package.json'), packageJSON => {
packageJSON.version = version;
});
for (const packageName of ['playwright-chromium', 'playwright-firefox', 'playwright-webkit', 'playwright']) {
updatePackage(path.join(__dirname, '..', 'packages', packageName, 'package.json'), packageJSON => {
packageJSON.version = version;
packageJSON.dependencies['playwright-core'] = `=${version}`;
});
}
function updatePackage(packageJSONPath, transform) {
console.log(`Updating ${packageJSONPath} to ${version}.`);
const packageJSON = JSON.parse(fs.readFileSync(packageJSONPath, 'utf8'));