├── bin ├── detect └── compile ├── test └── run.sh ├── .github └── workflows │ └── ci.yml └── README.md /bin/detect: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "Playwright" 4 | exit 0 -------------------------------------------------------------------------------- /test/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | mktmpdir() { 4 | dir=$(mktemp -t testXXXXX) 5 | rm -rf $dir 6 | mkdir $dir 7 | echo $dir 8 | } 9 | 10 | main() { 11 | bp_dir=$(mktmpdir) 12 | compile_dir=$(mktmpdir) 13 | env_dir=$(mktmpdir) 14 | cp -a "$(pwd)"/* ${bp_dir} 15 | ${bp_dir}/bin/compile ${compile_dir} ${2:-$(mktmpdir)} $env_dir 16 | } 17 | 18 | main 19 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: [ master ] 5 | pull_request: 6 | branches: [ master ] 7 | jobs: 8 | test_ubuntu_18: 9 | runs-on: ubuntu-18.04 10 | steps: 11 | - uses: actions/checkout@v2 12 | - run: bash test/run.sh 13 | env: 14 | STACK: heroku-18 15 | test_ubuntu_20: 16 | runs-on: ubuntu-20.04 17 | steps: 18 | - uses: actions/checkout@v2 19 | - run: bash test/run.sh 20 | env: 21 | STACK: heroku-20 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Heroku Playwright Buildpack 2 | 3 | This buildpack installs all the needed dependencies to use Playwright with Chromium and Firefox on Heroku. 4 | 5 | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/mxschmitt/heroku-playwright-example) 6 | 7 | ## Usage 8 | 9 | For using this buildpack, you have to add the buildpack **before** installing your Node.js dependencies. 10 | 11 | ```txt 12 | heroku buildpacks:set https://github.com/mxschmitt/heroku-playwright-buildpack.git -a my-app 13 | ``` 14 | 15 | For a full example, see [here](https://github.com/mxschmitt/heroku-playwright-example) a usage with the Express library. 16 | 17 | It's common to use the `PLAYWRIGHT_BUILDPACK_BROWSERS` environment variable which accepts a comma-separated list of the browser names (`chromium`, `firefox`, `webkit`). By default, it's installing the dependencies for all the browsers. To only install Chromium dependencies for example, just set it to `chromium`. This will reduce the slug size in the end too. 18 | 19 | You should also install the browser specific NPM packages like `playwright-chromium.` to reduce the slug size. 20 | 21 | ## Examples 22 | 23 | ### Chromium 24 | 25 | For using Chromium, it's **necessary** to use `chromiumSandbox: false` in the launch options, since on Heroku is no support for the Chromium sandbox. 26 | 27 | ```javascript 28 | const { chromium } = require("playwright-chromium"); 29 | 30 | (async () => { 31 | const browser = await chromium.launch({ chromiumSandbox: false }); 32 | const context = await browser.newContext(); 33 | const page = await context.newPage(); 34 | await page.goto("http://whatsmyuseragent.org/"); 35 | await page.screenshot({ path: `chromium.png` }); 36 | await browser.close(); 37 | })(); 38 | ``` 39 | 40 | ### Firefox 41 | 42 | For Firefox, you can refer to the official examples, no need to adjust any configurations. 43 | 44 | ```javascript 45 | const { firefox } = require("playwright-firefox"); 46 | 47 | (async () => { 48 | const browser = await chromium.launch(); 49 | const context = await browser.newContext(); 50 | const page = await context.newPage(); 51 | await page.goto("http://whatsmyuseragent.org/"); 52 | await page.screenshot({ path: `firefox.png` }); 53 | await browser.close(); 54 | })(); 55 | ``` 56 | 57 | ## Best practises 58 | 59 | It's common to only install the [browser-specific NPM packages](https://playwright.dev/#version=v1.1.1&path=docs%2Finstallation.md&q=download-single-browser-binary), which will reduce installation time and slug size on Heroku in the end, that should fix also the error that the slug size is too large. 60 | 61 | If you encounter this error at runtime, it means that you are missing the chromium binary, which can be installed with `playwright install chromium`. 62 | 63 | ``` 64 | browserType.launch: Executable doesn't exist at /app/node_modules/playwright-core/.local-browsers/chromium-1012/chrome-linux/chrome 65 | ╔═════════════════════════════════════════════════════════════════════════╗ 66 | ║ Looks like Playwright Test or Playwright was just installed or updated. ║ 67 | ║ Please run the following command to download new browsers: ║ 68 | ║ ║ 69 | ║ npx playwright install ║ 70 | ║ ║ 71 | ║ <3 Playwright Team ║ 72 | ╚═════════════════════════════════════════════════════════════════════════╝ 73 | ``` 74 | 75 | You can incorporate this into Heroku's build step by including this script in `package.json`. 76 | 77 | ``` 78 | "scripts": { 79 | "heroku-cleanup": "yarn run playwright install [chromium | webkit | firefox]" 80 | } 81 | ``` 82 | -------------------------------------------------------------------------------- /bin/compile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | BUILD_DIR=$1 6 | CACHE_DIR=$2 7 | ENV_DIR=$3 8 | BP_DIR=`cd $(dirname $0); cd ..; pwd` 9 | 10 | error() { 11 | echo " ! $*" >&2 12 | exit 1 13 | } 14 | 15 | topic() { 16 | echo "-----> $*" 17 | } 18 | 19 | install_system_deps() { 20 | topic "Installing System Dependencies" 21 | 22 | APT_BUILDPACK="https://github.com/heroku/heroku-buildpack-apt" 23 | local buildpack_tmpdir=$(mktemp -d) 24 | cd $buildpack_tmpdir && git clone $APT_BUILDPACK . 25 | 26 | local build_tmpdir=$(mktemp -d) 27 | mkdir -p $build_tmpdir 28 | 29 | SUPPORTED_BROWSERS=${PLAYWRIGHT_BUILDPACK_BROWSERS:-chromium,firefox,webkit} 30 | echo "Installing Playwright dependencies (env: PLAYWRIGHT_BUILDPACK_BROWSERS) for $SUPPORTED_BROWSERS." 31 | 32 | if [[ "$SUPPORTED_BROWSERS" == *"chromium"* ]]; then 33 | cat << EOF >>$build_tmpdir/Aptfile 34 | # Chromium dependencies 35 | libnspr4 36 | libnss3 37 | libxss1 38 | libasound2 39 | fonts-noto-color-emoji 40 | libgbm1 41 | libatk-bridge2.0-0 42 | libxkbcommon0 43 | libxrandr2 44 | libatspi2.0-0 45 | libxshmfence-dev 46 | EOF 47 | fi 48 | 49 | if [[ "$SUPPORTED_BROWSERS" == *"firefox"* ]]; then 50 | cat << EOF >>$build_tmpdir/Aptfile 51 | # Firefox dependencies 52 | libdbus-glib-1-2 53 | libxt6 54 | libsm6 55 | libice6 56 | libx11-xcb1 57 | libxcursor1 58 | libxi6 59 | EOF 60 | fi 61 | 62 | if [[ "$SUPPORTED_BROWSERS" == *"firefox"* || "$SUPPORTED_BROWSERS" == *"chromium"* ]]; then 63 | cat << EOF >>$build_tmpdir/Aptfile 64 | # Needed by Firefox and Chromium 65 | libgdk-pixbuf2.0-0 66 | libcairo-gobject2 67 | libpulse0 68 | libusb-1.0-0 69 | libatk1.0-0 70 | libxcomposite1 71 | libxdamage1 72 | libxfixes3 73 | libgtk-3-0 74 | EOF 75 | fi 76 | 77 | case "$STACK" in 78 | "heroku-18") 79 | cat << EOF >>$build_tmpdir/Aptfile 80 | libvpx5 81 | EOF 82 | ;; 83 | "heroku-20") 84 | cat << EOF >>$build_tmpdir/Aptfile 85 | libvpx6 86 | EOF 87 | ;; 88 | "heroku-22") 89 | cat << EOF >>$build_tmpdir/Aptfile 90 | libvpx7 91 | EOF 92 | ;; 93 | *) 94 | error "STACK must be 'heroku-18', 'heroku-20', or 'heroku-22'" 95 | esac 96 | 97 | local cache_tmpdir=$(mktemp -d) 98 | 99 | HOME=/app $buildpack_tmpdir/bin/compile $build_tmpdir $cache_tmpdir 100 | if [ $? -ne 0 ]; then 101 | rm -rf $buildpack_tmpdir $build_tmpdir $cache_tmpdir 102 | error "Could not install dependencies" 103 | fi 104 | mv $build_tmpdir/.apt $BUILD_DIR 105 | # mv file in case user is using .profile.d 106 | mkdir -p $BUILD_DIR/.profile.d/ 107 | mv $build_tmpdir/.profile.d/000_apt.sh $BUILD_DIR/.profile.d/ 108 | 109 | rm -rf $buildpack_tmpdir $build_tmpdir $cache_tmpdir 110 | } 111 | 112 | configure_export_env() { 113 | topic "Writing profile script" 114 | mkdir -p $BUILD_DIR/.profile.d 115 | cat << EOF >$BUILD_DIR/.profile.d/001_playwright.sh 116 | export PLAYWRIGHT_BROWSERS_PATH=0 117 | # They are not referenced correctly for ffmpeg 118 | export LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:/app/.apt/usr/lib/x86_64-linux-gnu/pulseaudio/:/app/.apt/lib/x86_64-linux-gnu/ 119 | EOF 120 | 121 | # Give environment variable to other buildpacks 122 | echo "export PLAYWRIGHT_BROWSERS_PATH=0" >> "$BP_DIR/export" 123 | export PLAYWRIGHT_BROWSERS_PATH=0 124 | } 125 | 126 | # See here as a reference: https://devcenter.heroku.com/articles/buildpack-api#bin-compile 127 | export_env_dir() { 128 | env_dir=$1 129 | acceptlist_regex=${2:-''} 130 | denylist_regex=${3:-'^(PATH|GIT_DIR|CPATH|CPPATH|LD_PRELOAD|LIBRARY_PATH)$'} 131 | if [ -d "$env_dir" ]; then 132 | for e in $(ls $env_dir); do 133 | echo "$e" | grep -E "$acceptlist_regex" | grep -qvE "$denylist_regex" && 134 | export "$e=$(cat $env_dir/$e)" 135 | : 136 | done 137 | fi 138 | } 139 | 140 | export_env_dir "$ENV_DIR" PLAYWRIGHT_BUILDPACK_BROWSERS 141 | install_system_deps 142 | configure_export_env 143 | --------------------------------------------------------------------------------