diff --git a/.appveyor.yml b/.appveyor.yml
deleted file mode 100644
index 81fd79b7..00000000
--- a/.appveyor.yml
+++ /dev/null
@@ -1,64 +0,0 @@
-environment:
-  global:
-    RUSTFLAGS: -Ctarget-feature=+crt-static
-    RUST_BACKTRACE: 1
-  matrix:
-    - TARGET: x86_64-pc-windows-msvc
-      DEPLOY: 1
-
-install:
-  - ps: Install-Product node 10
-  - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
-  - rustup-init.exe -y --default-host x86_64-pc-windows-msvc --default-toolchain stable
-  - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
-  - rustc -V
-  - cargo -V
-  - appveyor-retry appveyor DownloadFile https://github.com/mozilla/sccache/releases/download/0.2.7/sccache-0.2.7-x86_64-pc-windows-msvc.tar.gz
-  - tar xzf sccache-0.2.7-x86_64-pc-windows-msvc.tar.gz
-  - set PATH=%PATH%;%CD%/sccache-0.2.7-x86_64-pc-windows-msvc
-  - set RUSTC_WRAPPER=sccache
-
-build: false
-
-test_script:
-  - rustup target add wasm32-unknown-unknown
-  - npm install
-  - cargo test
-  - cargo build --release -p wasm-bindgen-cli
-  - where chromedriver
-  - set CHROMEDRIVER=C:\Tools\WebDriver\chromedriver.exe
-  - cargo test -p js-sys --target wasm32-unknown-unknown
-  - cargo test -p webidl-tests --target wasm32-unknown-unknown
-  # Try just a few features for `web-sys`, unfortunately the whole crate blows
-  # system command line limits meaning we can't even spawn rustc to enable all
-  # the features.
-  - cargo build --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --features "Node Window Document"
-
-branches:
-  only:
-    - master
-    - /^\d/
-
-before_deploy:
-  - ps: |
-        $NAME = "wasm-bindgen-${env:APPVEYOR_REPO_TAG_NAME}-${env:TARGET}"
-        New-Item -Path $NAME -ItemType directory
-        Copy-Item target/release/wasm-bindgen.exe "${NAME}/"
-        Copy-Item target/release/wasm2es6js.exe "${NAME}/"
-        Copy-Item target/release/wasm-bindgen-test-runner.exe "${NAME}/"
-        Copy-Item LICENSE-MIT "${NAME}/"
-        Copy-Item LICENSE-APACHE "${NAME}/"
-        Copy-Item README.md "${NAME}/"
-        7z a -ttar "${NAME}.tar" "${NAME}"
-        7z a "${NAME}.tar.gz" "${NAME}.tar"
-        Push-AppveyorArtifact "${NAME}.tar.gz"
-
-deploy:
-  artifact: /.*\.tar.gz/
-  auth_token:
-    secure: dtHSvbZkdAFtL0J5YrSw8DpxjfYuHWgqD1SupmJT/yfYSjEBiX55RFXRoqBM2tx1
-  description: ''
-  on:
-    appveyor_repo_tag: true
-  provider: GitHub
-
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index ed2b753d..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,256 +0,0 @@
-language: rust
-sudo: false
-
-INSTALL_NODE_VIA_NVM: &INSTALL_NODE_VIA_NVM
-  |
-    rustup target add wasm32-unknown-unknown
-    curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
-    source ~/.nvm/nvm.sh
-    nvm install v10.9
-
-INSTALL_GECKODRIVER: &INSTALL_GECKODRIVER
-  |
-    curl --retry 5 -LO https://github.com/mozilla/geckodriver/releases/download/v0.21.0/geckodriver-v0.21.0-linux64.tar.gz
-    tar xf geckodriver-v0.21.0-linux64.tar.gz
-
-DEPLOY_TO_GITHUB: &DEPLOY_TO_GITHUB
-  before_deploy:
-    |
-      name="wasm-bindgen-$TRAVIS_TAG-$TARGET"
-      mkdir "$name"
-      cp "target/$TARGET/release/wasm-bindgen" "$name/"
-      cp "target/$TARGET/release/wasm2es6js" "$name/"
-      cp "target/$TARGET/release/wasm-bindgen-test-runner" "$name/"
-      cp README.md LICENSE-MIT LICENSE-APACHE "$name/"
-      tar czvf "$name.tar.gz" "$name"
-  deploy:
-    api_key:
-      secure: "qCiELnEnvyKpWHDttgTNf+ElZGbWlvthu5aOIj5nYfov+h6g1+mkWnDFP6at/WPlE78zE/f/z/dL2KB2I7w/cxH/T4P1nWh0A9DvrpY6hqWkK2pgN5dPeWE/a4flI7AdH0A6wMRw7m00uMgDjlzN78v7XueccpJCxSO5allQN5jweAQvMX2QA07TbLRJc7Lq6lfVwSf8OfrcO8qCbcIzJTsC4vtbh6jkUYg1OAaU2tAYlskBy9ZYmHWCExIAu/zxzcJY9OpApPD9Ea4CyrsfjniAyRBJ87Weh/sP4XhiWeRPVmvA4HAzv4Pps9ps+Ar5QmsX53rhKQ3id7/VPR8ggaAHxrYUiJPvJRtbP6cKKOlDiK0ooP+vI4vjxWeNVj9ibEolSYOlT0ENIvPK1BppA6VgAoJOjwPr0Q16Ma4AmvLkIkowJiXCm2Jlje/5c0vPEAGJVgUtkj3jFQzgXwyEMpzxUlhHmYpmnfeaM0tK/Kiiwe1monL/ydMlyfV55kNylylCg+XoTnf420AFChKbD4DM5Z7ZsjU9g8fF3LUoN0sKpmLDp+GvwjLi9YtGogWB71Q2MFp43MSL0YLshkyYYoZKrVMiy5J9hKNUxhT2jNEq53Z69syIHHMCxHL9GoAcuHxIKOA7uTMW0aCoyy2I+dfAKKsrUGwGYaNC5LZdUQI="
-    file_glob: true
-    file:
-      - wasm-bindgen-$TRAVIS_TAG-$TARGET.tar.gz
-    on:
-      tags: true
-    provider: releases
-    skip_cleanup: true
-  if: branch = master OR branch =~ /^\d/
-
-INSTALL_AWS: &INSTALL_AWS
-  |
-    pip install --user awscli
-    export PATH=$HOME/.local/bin:$PATH
-    mkdir -p ~/$TRAVIS_BUILD_NUMBER
-
-before_install:
-  - target=x86_64-unknown-linux-musl
-  - if [ "$TRAVIS_OS_NAME" = "osx" ]; then target=x86_64-apple-darwin; fi
-  - curl -L https://github.com/mozilla/sccache/releases/download/0.2.7/sccache-0.2.7-$target.tar.gz | tar xzf -
-  - export PATH=$PATH:`pwd`/sccache-0.2.7-$target
-  - export RUSTC_WRAPPER=sccache
-
-after_script:
-  - sccache -s
-
-matrix:
-  include:
-    # Tests for wasm-bindgen itself pass
-    - name: "test wasm-bindgen crate"
-      install:
-        - *INSTALL_NODE_VIA_NVM
-        - *INSTALL_GECKODRIVER
-        - export GECKODRIVER=`pwd`/geckodriver
-      script:
-        # Run a test or two that makes sure `#[wasm_bindgen]` works "reasonably"
-        # on non-wasm platforms
-        - cargo test
-        # Run the main body of the test suite
-        - cargo test --target wasm32-unknown-unknown
-        # Make sure the anyref pass at least compiles even if it doesn't run
-        - NODE_ARGS=/dev/null WASM_BINDGEN_ANYREF=1 cargo test --target wasm32-unknown-unknown --test wasm
-        # Rerun the test suite but disable `--debug` in generated JS
-        - WASM_BINDGEN_NO_DEBUG=1 cargo test --target wasm32-unknown-unknown
-        # Make sure our serde tests work
-        - cargo test --target wasm32-unknown-unknown --features serde-serialize
-        # Make sure the `std` feature works if disabled
-        - cargo test --target wasm32-unknown-unknown -p no-std
-        # Make sure the `wasm-bindgen-futures` tests pass.
-        - cargo test -p wasm-bindgen-futures
-        - cargo test -p wasm-bindgen-futures --target wasm32-unknown-unknown
-      addons:
-        firefox: latest
-      if: branch = master
-
-    # Tests the `nightly` feature of wasm-bindgen
-    - rust: nightly
-      name: "test: wasm-bindgen crate nightly feature"
-      install:
-        - *INSTALL_NODE_VIA_NVM
-      script:
-        - cargo test --target wasm32-unknown-unknown --features nightly --test wasm
-      if: branch = master
-
-    # All examples work
-    - name: "examples - almost all examples"
-      install:
-        - *INSTALL_NODE_VIA_NVM
-        - *INSTALL_AWS
-        - npm install
-        - curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f
-      script:
-        - cargo build -p wasm-bindgen-cli
-        - ln -snf target/debug/wasm-bindgen $HOME/.cargo/wasm-bindgen
-        - |
-          for dir in `ls examples | grep -v README | grep -v asm.js | grep -v raytrace | grep -v without-a-bundler`; do
-            (cd examples/$dir &&
-             ln -fs ../../node_modules . &&
-             npm run build -- --output-path $HOME/$TRAVIS_BUILD_NUMBER/exbuild/$dir) || exit 1;
-          done
-        - if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then aws s3 sync --quiet ~/$TRAVIS_BUILD_NUMBER s3://wasm-bindgen-ci/$TRAVIS_BUILD_NUMBER; fi
-      if: branch = master
-    - rust: nightly
-      name: "examples - raytracer"
-      install:
-        - *INSTALL_AWS
-        - rustup component add rust-src
-        - curl -L https://github.com/japaric/xargo/releases/download/v0.3.13/xargo-v0.3.13-x86_64-unknown-linux-musl.tar.gz | tar xzf -
-        - export PATH=$PATH:`pwd`
-      script:
-        - sed -i 's/python/#python/' examples/raytrace-parallel/build.sh
-        - (cd examples/raytrace-parallel && ./build.sh)
-        - dst=$HOME/$TRAVIS_BUILD_NUMBER/exbuild/raytrace-parallel
-        - mkdir -p $dst
-        - cp examples/raytrace-parallel/*.{js,html,wasm} $dst
-        - if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then aws s3 sync ~/$TRAVIS_BUILD_NUMBER s3://wasm-bindgen-ci/$TRAVIS_BUILD_NUMBER; fi
-      if: branch = master
-
-    # The `cli-support` crate's tests pass
-    - name: "test cli-support crate"
-      script: cargo test -p wasm-bindgen-cli-support
-      if: branch = master
-
-    # The `web-sys` crate's tests pass
-    - name: "test web-sys crate"
-      install:
-        - *INSTALL_NODE_VIA_NVM
-        - *INSTALL_GECKODRIVER
-      script:
-        - export RUST_LOG=wasm_bindgen_test_runner
-        # Test out builds with just a few features
-        - cargo build --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown
-        - cargo build --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --features Node
-        - cargo build --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --features Element
-        - cargo build --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --features Window
-
-        # Now run all the tests with all the features
-        - GECKODRIVER=`pwd`/geckodriver cargo test --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --all-features
-      addons:
-        firefox: latest
-        chrome: stable
-      if: branch = master
-
-    # The `js-sys` crate's tests pass
-    - name: "test js-sys crate"
-      install:
-        - *INSTALL_NODE_VIA_NVM
-        - *INSTALL_GECKODRIVER
-      script:
-        - export RUST_LOG=wasm_bindgen_test_runner
-        - GECKODRIVER=`pwd`/geckodriver cargo test -p js-sys --target wasm32-unknown-unknown
-      addons:
-        firefox: latest
-        chrome: stable
-      if: branch = master
-
-    # WebIDL tests pass
-    - name: "test wasm-bindgen-webidl crate"
-      install: *INSTALL_NODE_VIA_NVM
-      script:
-        - cargo test -p wasm-bindgen-webidl
-        - cargo test -p webidl-tests --target wasm32-unknown-unknown
-      if: branch = master
-
-    # UI tests for the macro work just fine
-    - rust: nightly
-      name: "test ui tests"
-      script: cargo test -p ui-tests
-      if: branch = master
-
-    # wasm-interpreter tests work alright
-    - name: "test wasm-bindgen-wasm-interpreter crate"
-      install:
-        - git clone https://github.com/WebAssembly/wabt
-        - mkdir -p wabt/build
-        - (cd wabt/build && cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=sccache -DCMAKE_CXX_COMPILER_ARG1=c++ -DBUILD_TESTS=OFF && cmake --build . -- -j4)
-        - export PATH=$PATH:`pwd`/wabt/build
-      script: cargo test -p wasm-bindgen-wasm-interpreter
-      if: branch = master
-
-    # Dist linux binary
-    - name: "dist: Linux (x86_64-unknown-linux-musl)"
-      env: JOB=dist-linux TARGET=x86_64-unknown-linux-musl
-      before_script: rustup target add $TARGET
-      script:
-        - cargo build --manifest-path crates/cli/Cargo.toml --release --target $TARGET --features vendored-openssl
-        # no need to ship debuginfo to users
-        - strip -g target/$TARGET/release/wasm-bindgen
-        - strip -g target/$TARGET/release/wasm-bindgen-test-runner
-        - strip -g target/$TARGET/release/wasm2es6js
-      addons:
-        apt:
-          packages:
-          - musl-tools
-      <<: *DEPLOY_TO_GITHUB
-
-    # Dist OSX binary
-    - name: "dist: OSX (x86_64-apple-darwin)"
-      os: osx
-      env: JOB=dist-osx MACOSX_DEPLOYMENT_TARGET=10.7 TARGET=x86_64-apple-darwin
-      script: cargo build --manifest-path crates/cli/Cargo.toml --release --target $TARGET
-      <<: *DEPLOY_TO_GITHUB
-
-    # Build mdbook documentation
-    - name: "doc: Guide documentation"
-      install:
-        - mkdir -p $HOME/mdbook
-        - curl -L https://github.com/rust-lang-nursery/mdBook/releases/download/v0.2.1/mdbook-v0.2.1-x86_64-unknown-linux-musl.tar.gz | tar xzf - -C $HOME/mdbook
-        - export PATH=$PATH:$HOME/mdbook
-        - *INSTALL_AWS
-      script:
-        - (cd guide && mdbook build)
-        - rm -rf ~/$TRAVIS_BUILD_NUMBER
-        - mv guide/book ~/$TRAVIS_BUILD_NUMBER
-        - if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then aws s3 sync --quiet ~/$TRAVIS_BUILD_NUMBER s3://wasm-bindgen-ci/$TRAVIS_BUILD_NUMBER; fi
-      if: branch = master
-
-    # Build API documentation
-    - rust: stable
-      name: "doc: API documentation"
-      install: *INSTALL_AWS
-      script:
-        - cargo doc --no-deps --features 'nightly serde-serialize'
-        - cargo doc --no-deps --manifest-path crates/js-sys/Cargo.toml
-        - cargo doc --no-deps --manifest-path crates/futures/Cargo.toml
-        - cargo doc --no-deps --manifest-path crates/web-sys/Cargo.toml --all-features
-        - mv target/doc ~/$TRAVIS_BUILD_NUMBER/api
-        - if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then aws s3 sync --quiet ~/$TRAVIS_BUILD_NUMBER s3://wasm-bindgen-ci/$TRAVIS_BUILD_NUMBER; fi
-      if: branch = master
-
-    # Take compiled examples and mdbook/API docs and deploy them to gh-pages
-    - stage: deploy-gh-pages
-      install: *INSTALL_AWS
-      script:
-        - aws s3 sync --quiet s3://wasm-bindgen-ci/$TRAVIS_BUILD_NUMBER ~/$TRAVIS_BUILD_NUMBER
-        - mv ~/$TRAVIS_BUILD_NUMBER doc
-      deploy:
-        provider: pages
-        skip-cleanup: true
-        github-token: $GITHUB_TOKEN  # Set in travis-ci.org dashboard, marked secure
-        local-dir: doc
-        keep-history: false
-      after_deploy:
-        - aws s3 rm --recursive s3://rust-lang-ci-sccache2/$TRAVIS_BUILD_NUMBER
-      if: branch = master AND type != pull_request
-
-notifications:
-  email:
-    on_success: never
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
new file mode 100644
index 00000000..2753298d
--- /dev/null
+++ b/azure-pipelines.yml
@@ -0,0 +1,342 @@
+trigger:
+  branches:
+    include:
+      - refs/heads/master
+      - refs/tags/*
+
+jobs:
+  - job: test_wasm_bindgen
+    displayName: "Run wasm-bindgen crate tests (unix)"
+    steps:
+      - template: ci/azure-install-rust.yml
+      - template: ci/azure-install-node.yml
+      - template: ci/azure-install-geckodriver.yml
+      - template: ci/azure-install-sccache.yml
+      - script: cargo test
+        displayName: "Builds on native"
+      - script: cargo test --target wasm32-unknown-unknown
+        displayName: "Crate test suite"
+      - script: WASM_BINDGEN_NO_DEBUG=1 cargo test --target wasm32-unknown-unknown
+        displayName: "Crate test suite (no debug)"
+      - script: NODE_ARGS=/dev/null WASM_BINDGEN_ANYREF=1 cargo test --target wasm32-unknown-unknown --test wasm
+        displayName: "Anyref test suite builds"
+      - script: cargo test --target wasm32-unknown-unknown --features serde-serialize
+        displayName: "Crate test suite (with serde)"
+      - script: cargo test --target wasm32-unknown-unknown -p no-std
+        displayName: "Crate test suite (no_std)"
+      - script: cargo test -p wasm-bindgen-futures
+        displayName: "Futures test suite on native"
+      - script: cargo test -p wasm-bindgen-futures --target wasm32-unknown-unknown
+        displayName: "Futures test suite on wasm"
+
+  - job: test_wasm_bindgen_windows
+    displayName: "Run wasm-bindgen crate tests (Windows)"
+    pool:
+      vmImage: vs2017-win2016
+    steps:
+      - template: ci/azure-install-rust.yml
+      - template: ci/azure-install-node.yml
+      - template: ci/azure-install-geckodriver.yml
+      - template: ci/azure-install-sccache.yml
+      - script: cargo test --target wasm32-unknown-unknown
+        displayName: "wasm-bindgen test suite"
+      - script: cargo test --target wasm32-unknown-unknown -p js-sys
+        displayName: "js-sys test suite"
+      - script: cargo test --target wasm32-unknown-unknown -p webidl-tests
+        displayName: "webidl-tests test suite"
+      - script: cargo build --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --features "Node Window Document"
+        displayName: "web-sys build"
+
+  - job: test_wasm_bindgen_nightly
+    displayName: "Run wasm-bindgen crate tests (nightly)"
+    steps:
+      - template: ci/azure-install-rust.yml
+        parameters:
+          toolchain: nightly
+      - template: ci/azure-install-node.yml
+      - template: ci/azure-install-sccache.yml
+      - script: cargo test --target wasm32-unknown-unknown --features nightly --test wasm
+
+  - job: test_cli_support
+    displayName: "Run wasm-bindgen-cli-support crate tests"
+    steps:
+      - template: ci/azure-install-rust.yml
+      - template: ci/azure-install-sccache.yml
+      - script: cargo test -p wasm-bindgen-cli-support
+
+  - job: test_web_sys
+    displayName: "Run web-sys crate tests"
+    steps:
+      - template: ci/azure-install-rust.yml
+      - template: ci/azure-install-node.yml
+      - template: ci/azure-install-geckodriver.yml
+      - template: ci/azure-install-sccache.yml
+      - script: cargo build --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown
+      - script: cargo build --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --features Node
+      - script: cargo build --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --features Element
+      - script: cargo build --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --features Window
+      - script: cargo test --manifest-path crates/web-sys/Cargo.toml --target wasm32-unknown-unknown --all-features
+
+  - job: test_js_sys
+    displayName: "Run js-sys crate tests"
+    steps:
+      - template: ci/azure-install-rust.yml
+      - template: ci/azure-install-node.yml
+      - template: ci/azure-install-geckodriver.yml
+      - template: ci/azure-install-sccache.yml
+      - script: cargo test -p js-sys --target wasm32-unknown-unknown
+
+  - job: test_webidl
+    displayName: "Run wasm-bindgen-webidl crate tests"
+    steps:
+      - template: ci/azure-install-rust.yml
+      - template: ci/azure-install-node.yml
+      - template: ci/azure-install-sccache.yml
+      - script: cargo test -p wasm-bindgen-webidl
+      - script: cargo test -p webidl-tests --target wasm32-unknown-unknown
+
+  - job: test_ui
+    displayName: "Run UI tests"
+    steps:
+      - template: ci/azure-install-rust.yml
+        parameters:
+          toolchain: nightly
+      - template: ci/azure-install-node.yml
+      - template: ci/azure-install-sccache.yml
+      - script: cargo test -p ui-tests
+
+  - job: test_wasm_interpreter
+    displayName: "Run wasm-bindgen-wasm-interpreter tests"
+    steps:
+      - template: ci/azure-install-rust.yml
+      - template: ci/azure-install-sccache.yml
+      - script: |
+          git clone https://github.com/WebAssembly/wabt
+          mkdir -p wabt/build
+          cd wabt/build
+          cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=off -DCMAKE_CXX_COMPILER_LAUNCHER=$RUSTC_WRAPPER
+          cmake --build . -- -j$(nproc)
+          echo "##vso[task.setvariable variable=PATH;]$PATH:$PWD"
+      - script: cargo test -p wasm-bindgen-wasm-interpreter
+
+  - job: build_examples
+    displayName: "Build almost all examples"
+    steps:
+      - template: ci/azure-install-rust.yml
+      - template: ci/azure-install-sccache.yml
+      - script: npm install
+      - script: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f
+      - script: cargo build -p wasm-bindgen-cli
+      - script: ln -snf target/debug/wasm-bindgen $HOME/.cargo/wasm-bindgen
+      - script: |
+          for dir in `ls examples | grep -v README | grep -v asm.js | grep -v raytrace | grep -v without-a-bundler`; do
+            (cd examples/$dir &&
+            ln -fs ../../node_modules . &&
+            npm run build -- --output-path $BUILD_ARTIFACTSTAGINGDIRECTORY/exbuild/$dir) || exit 1;
+          done
+      - task: PublishPipelineArtifact@0
+        inputs:
+          artifactName: examples1
+          targetPath: '$(Build.ArtifactStagingDirectory)'
+
+  - job: build_raytrace
+    displayName: "Build raytrace examples"
+    steps:
+      - template: ci/azure-install-rust.yml
+        parameters:
+          toolchain: nightly
+      - template: ci/azure-install-sccache.yml
+      - script: rustup component add rust-src
+        displayName: "install rust-src"
+      - script: |
+          curl -L https://github.com/japaric/xargo/releases/download/v0.3.13/xargo-v0.3.13-x86_64-unknown-linux-musl.tar.gz | tar xzf -
+          echo "##vso[task.setvariable variable=PATH;]$PATH:$PWD"
+        displayName: "install xargo"
+      - script: |
+          sed -i 's/python/#python/' examples/raytrace-parallel/build.sh
+          (cd examples/raytrace-parallel && ./build.sh)
+          cp examples/raytrace-parallel/*.{js,html,wasm} $BUILD_ARTIFACTSTAGINGDIRECTORY
+        displayName: "build example"
+      - task: PublishPipelineArtifact@0
+        inputs:
+          artifactName: examples2
+          targetPath: '$(Build.ArtifactStagingDirectory)'
+
+  - job: dist_linux
+    displayName: "Dist Linux binary"
+    steps:
+      - template: ci/azure-install-rust.yml
+      - template: ci/azure-install-sccache.yml
+      - script: rustup target add x86_64-unknown-linux-musl
+      - script: |
+          sudo apt update -y
+          sudo apt install musl-tools -y
+        displayName: "Install musl-tools"
+      - script: |
+          set -ex
+          cargo build --manifest-path crates/cli/Cargo.toml --target x86_64-unknown-linux-musl --features vendored-openssl --release
+          strip -g target/x86_64-unknown-linux-musl/release/wasm-bindgen
+          strip -g target/x86_64-unknown-linux-musl/release/wasm-bindgen-test-runner
+          strip -g target/x86_64-unknown-linux-musl/release/wasm2es6js
+      - template: ci/azure-create-tarball.yml
+        parameters:
+          artifacts: target/x86_64-unknown-linux-musl/release
+          name: dist_linux
+
+  - job: dist_darwin
+    displayName: "Dist Darwin binary"
+    pool:
+      vmImage: macOS-10.13
+    steps:
+      - template: ci/azure-install-rust.yml
+      - template: ci/azure-install-sccache.yml
+      - script: cargo build --manifest-path crates/cli/Cargo.toml --release
+        env:
+          MACOSX_DEPLOYMENT_TARGET: 10.7
+      - template: ci/azure-create-tarball.yml
+        parameters:
+          name: dist_darwin
+
+  - job: dist_windows
+    displayName: "Dist Windows binary"
+    pool:
+      vmImage: vs2017-win2016
+    steps:
+      - template: ci/azure-install-rust.yml
+      - template: ci/azure-install-sccache.yml
+      - script: cargo build --manifest-path crates/cli/Cargo.toml --release
+        env:
+          RUSTFLAGS: -Ctarget-feature=+crt-static
+      - template: ci/azure-create-tarball.yml
+        parameters:
+          name: dist_windows
+      - script: "%RUSTC_WRAPPER% -s"
+      - script: cat sccache.log
+
+  - job: doc_book
+    displayName: "Doc - build the book"
+    steps:
+      - script: |
+          mkdir $HOME/mdbook
+          curl -L https://github.com/rust-lang-nursery/mdBook/releases/download/v0.2.1/mdbook-v0.2.1-x86_64-unknown-linux-musl.tar.gz | tar xzf - -C $HOME/mdbook
+          echo "##vso[task.setvariable variable=PATH;]$PATH:$HOME/mdbook"
+        displayName: "Install mdbook"
+      - script: (cd guide && mdbook build)
+      - task: PublishPipelineArtifact@0
+        inputs:
+          artifactName: doc_book
+          targetPath: guide/book
+
+  - job: doc_api
+    displayName: "Doc - build the API documentation"
+    steps:
+      - template: ci/azure-install-rust.yml
+      - template: ci/azure-install-sccache.yml
+      - script: cargo doc --no-deps --features 'nightly serde-serialize'
+        displayName: "Document wasm-bindgen"
+      - script: cargo doc --no-deps --manifest-path crates/js-sys/Cargo.toml
+        displayName: "Document js-sys"
+      - script: cargo doc --no-deps --manifest-path crates/web-sys/Cargo.toml --all-features
+        displayName: "Document web-sys"
+      - script: cargo doc --no-deps --manifest-path crates/futures/Cargo.toml
+        displayName: "Document wasm-bindgen-futures"
+      - script: cargo doc --no-deps --manifest-path crates/futures/Cargo.toml
+        displayName: "Document wasm-bindgen-futures"
+      # Make a tarball even though a zip is uploaded, it looks like the tarball
+      # makes the uploading step much speedier.
+      - script: tar czvf $BUILD_ARTIFACTSTAGINGDIRECTORY/docs.tar.gz target/doc
+      - task: PublishPipelineArtifact@0
+        inputs:
+          artifactName: doc_api
+          targetPath: target/doc
+
+  - job: deploy
+    dependsOn:
+      - doc_api
+      - doc_book
+      - dist_linux
+      - dist_darwin
+      - dist_windows
+      - build_examples
+      - build_raytrace
+    displayName: "Deploy everything"
+    steps:
+      - template: ci/azure-install-rust.yml
+      - task: DownloadPipelineArtifact@0
+        displayName: "Download docs - api"
+        inputs:
+          artifactName: doc_api
+          targetPath: gh-pages/api
+      - task: DownloadPipelineArtifact@0
+        displayName: "Download docs - book"
+        inputs:
+          artifactName: doc_book
+          targetPath: gh-pages
+      - task: DownloadPipelineArtifact@0
+        displayName: "Download examples"
+        inputs:
+          artifactName: examples1
+          targetPath: gh-pages
+      - task: DownloadPipelineArtifact@0
+        displayName: "Download examples - raytracer"
+        inputs:
+          artifactName: examples2
+          targetPath: gh-pages/exbuild/raytrace-parallel
+      - task: DownloadPipelineArtifact@0
+        displayName: "Download dist - windows"
+        inputs:
+          artifactName: dist_windows
+          targetPath: tmp/windows
+      - task: DownloadPipelineArtifact@0
+        displayName: "Download dist - linux"
+        inputs:
+          artifactName: dist_linux
+          targetPath: tmp/linux
+      - task: DownloadPipelineArtifact@0
+        displayName: "Download dist - darwin"
+        inputs:
+          artifactName: dist_darwin
+          targetPath: tmp/darwin
+      - script: |
+          set -ex
+          mkdir -p gh-release
+          find .
+          tag=`git describe --abbrev=0`
+          mk() {
+            target=$1
+            src=$2
+            name=wasm-bindgen-$tag-$target
+            mkdir -p tmp/$name
+            cp README.md \
+              LICENSE-MIT \
+              LICENSE-APACHE \
+              tmp/$src/wasm* \
+              tmp/$name/
+            chmod +x tmp/$name/wasm*
+            tar czvf gh-release/$name.tar.gz -C tmp $name
+          }
+          mk x86_64-unknown-linux-musl linux
+          mk x86_64-apple-darwin darwin
+          mk x86_64-pc-windows-msvc windows
+        displayName: "prepare the github releases tarball artifacts"
+      - task: PublishPipelineArtifact@0
+        displayName: "publish gh_release artifact"
+        inputs:
+          artifactName: gh_release
+          targetPath: gh-release
+      - task: PublishPipelineArtifact@0
+        displayName: "publish gh_pages artifact"
+        inputs:
+          artifactName: gh_pages
+          targetPath: gh-pages
+      - script: curl -LsSf https://git.io/fhJ8n | rustc - && (cd gh-pages && ../rust_out)
+        condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
+        env:
+          GITHUB_DEPLOY_KEY: $(GITHUB_DEPLOY_KEY)
+      - task: GithubRelease@0
+        #condition: condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))
+        displayName: 'Create GitHub Release'
+        inputs:
+          gitHubConnection: alexcrichton-oauth
+          repositoryName: alexcrichton/wasm-bindgen
+          assets: gh-release/*.tar.gz
diff --git a/ci/azure-create-tarball.yml b/ci/azure-create-tarball.yml
new file mode 100644
index 00000000..23a09851
--- /dev/null
+++ b/ci/azure-create-tarball.yml
@@ -0,0 +1,16 @@
+parameters:
+  artifacts: 'target/release'
+  name: ''
+
+steps:
+  - bash: |
+      set -ex
+      dst=$BUILD_ARTIFACTSTAGINGDIRECTORY
+      rm -f ${{ parameters.artifacts }}/wasm*.d
+      rm -f ${{ parameters.artifacts }}/wasm*.pdb
+      cp ${{ parameters.artifacts }}/wasm* $dst/
+    displayName: Create distribution tarball
+  - task: PublishPipelineArtifact@0
+    inputs:
+      artifactName: ${{ parameters.name }}
+      targetPath: '$(Build.ArtifactStagingDirectory)'
diff --git a/ci/azure-install-geckodriver.yml b/ci/azure-install-geckodriver.yml
new file mode 100644
index 00000000..723b81db
--- /dev/null
+++ b/ci/azure-install-geckodriver.yml
@@ -0,0 +1,14 @@
+steps:
+  - bash: |
+      curl --retry 5 -LO https://github.com/mozilla/geckodriver/releases/download/v0.21.0/geckodriver-v0.21.0-linux64.tar.gz
+      tar xf geckodriver-v0.21.0-linux64.tar.gz
+      echo "##vso[task.setvariable variable=GECKODRIVER;]$PWD/geckodriver"
+    displayName: "Download Geckodriver (Linux)"
+    condition: eq( variables['Agent.OS'], 'Linux' )
+
+  - powershell: |
+      Invoke-WebRequest https://github.com/mozilla/geckodriver/releases/download/v0.24.0/geckodriver-v0.24.0-win64.zip -OutFile gecko.zip
+      unzip gecko.zip
+      Write-Host "##vso[task.setvariable variable=GECKODRIVER;]$pwd\geckodriver.exe"
+    displayName: "Download Geckodriver (Windows)"
+    condition: eq( variables['Agent.OS'], 'Windows_NT' )
diff --git a/ci/azure-install-node.yml b/ci/azure-install-node.yml
new file mode 100644
index 00000000..e3cc4c1b
--- /dev/null
+++ b/ci/azure-install-node.yml
@@ -0,0 +1,6 @@
+steps:
+  - script: rustup target add wasm32-unknown-unknown
+    displayName: "Add WebAssembly target via rustup"
+  - task: NodeTool@0
+    inputs:
+      versionSpec: '10.x'
diff --git a/ci/azure-install-rust.yml b/ci/azure-install-rust.yml
new file mode 100644
index 00000000..dd4ac1c0
--- /dev/null
+++ b/ci/azure-install-rust.yml
@@ -0,0 +1,25 @@
+parameters:
+  toolchain: 'stable'
+
+steps:
+  - bash: |
+      curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain $TOOLCHAIN
+      echo "##vso[task.setvariable variable=PATH;]$PATH:$HOME/.cargo/bin"
+    displayName: Install rust - Unix
+    condition: ne( variables['Agent.OS'], 'Windows_NT' )
+    env:
+      TOOLCHAIN: ${{ parameters.toolchain }}
+
+  - script: |
+      curl -sSf -o rustup-init.exe https://win.rustup.rs
+      rustup-init.exe -y --default-toolchain %TOOLCHAIN%
+      echo "##vso[task.setvariable variable=PATH;]%PATH%;%USERPROFILE%\.cargo\bin"
+    displayName: Install rust - Windows
+    condition: eq( variables['Agent.OS'], 'Windows_NT' )
+    env:
+      TOOLCHAIN: ${{ parameters.toolchain }}
+
+  - script: |
+        rustc -Vv
+        cargo -V
+    displayName: Query rust and cargo versions
diff --git a/ci/azure-install-sccache.yml b/ci/azure-install-sccache.yml
new file mode 100644
index 00000000..87536433
--- /dev/null
+++ b/ci/azure-install-sccache.yml
@@ -0,0 +1,35 @@
+steps:
+  - bash: |
+      set -ex
+      curl -L https://github.com/mozilla/sccache/releases/download/0.2.8/sccache-0.2.8-x86_64-unknown-linux-musl.tar.gz | tar xzf -
+      sccache=`pwd`/sccache-0.2.8-x86_64-unknown-linux-musl/sccache
+      echo "##vso[task.setvariable variable=RUSTC_WRAPPER;]$sccache"
+    displayName: Install sccache - Linux
+    condition: eq( variables['Agent.OS'], 'Linux' )
+
+  - bash: |
+      set -ex
+      brew install openssl@1.1
+      curl -L https://github.com/mozilla/sccache/releases/download/0.2.8/sccache-0.2.8-x86_64-apple-darwin.tar.gz | tar xzf -
+      sccache=`pwd`/sccache-0.2.8-x86_64-apple-darwin/sccache
+      echo "##vso[task.setvariable variable=RUSTC_WRAPPER;]$sccache"
+    displayName: Install sccache - Darwin
+    condition: eq( variables['Agent.OS'], 'Darwin' )
+
+  - powershell: |
+      Invoke-WebRequest https://github.com/mozilla/sccache/releases/download/0.2.8/sccache-0.2.8-x86_64-pc-windows-msvc.tar.gz -OutFile sccache.tar.gz
+      tar xzf sccache.tar.gz
+      Write-Host "##vso[task.setvariable variable=RUSTC_WRAPPER;]$pwd/sccache-0.2.8-x86_64-pc-windows-msvc/sccache.exe"
+    displayName: Install sccache - Windows
+    condition: eq( variables['Agent.OS'], 'Windows_NT' )
+
+  - bash: |
+      set -ex
+      env
+      SCCACHE_ERROR_LOG=`pwd`/sccache.log RUST_LOG=debug $RUSTC_WRAPPER --start-server
+      $RUSTC_WRAPPER -s
+      cat sccache.log
+    displayName: "start sccache"
+    env:
+      AWS_ACCESS_KEY_ID: $(AWS_ACCESS_KEY_ID)
+      AWS_SECRET_ACCESS_KEY: $(AWS_SECRET_ACCESS_KEY)