Switch to Cirrus, turn off Travis
diff --git a/.cirrus.yml b/.cirrus.yml
new file mode 100644
index 0000000..e0093bb
--- /dev/null
+++ b/.cirrus.yml
@@ -0,0 +1,55 @@
+task:
+  container:
+    image: cirrusci/flutter:latest
+    cpu: 4
+    memory: 8G
+  upgrade_script:
+    - flutter channel master
+    - flutter upgrade
+    - git fetch origin master
+  activate_script: pub global activate flutter_plugin_tools
+  matrix:
+    - name: publishable
+      script: ./script/check_publish.sh
+    - name: test+format
+      install_script:
+        - wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
+        - sudo apt-add-repository "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-5.0 main"
+        - sudo apt-get update
+        - sudo apt-get install -y --allow-unauthenticated clang-format-5.0
+      format_script: ./script/incremental_build.sh format --travis --clang-format=clang-format-5.0
+      test_script: ./script/incremental_build.sh test
+    - name: analyze
+      script: ./script/incremental_build.sh analyze
+    - name: build-apks+java-test
+      env:
+        matrix:
+          PLUGIN_SHARDING: "--shardIndex 0 --shardCount 2"
+          PLUGIN_SHARDING: "--shardIndex 1 --shardCount 2"
+      script:
+        - ./script/incremental_build.sh build-examples --apk
+        - ./script/incremental_build.sh java-test  # must come after apk build
+
+task:
+  name: build-ipas
+  osx_instance:
+    image: high-sierra-xcode-9.4
+  env:
+    PATH: $PATH:/usr/local/bin
+    matrix:
+      PLUGIN_SHARDING: "--shardIndex 0 --shardCount 2"
+      PLUGIN_SHARDING: "--shardIndex 1 --shardCount 2"
+  setup_script:
+    - brew update
+    - brew install libimobiledevice
+    - brew install ideviceinstaller
+    - brew install ios-deploy
+    - pod repo update
+    - git clone https://github.com/flutter/flutter.git
+    - git fetch origin master
+    - export PATH=`pwd`/flutter/bin:`pwd`/flutter/bin/cache/dart-sdk/bin:$PATH
+    - flutter doctor
+    - pub global activate flutter_plugin_tools
+  build_script:
+    - export PATH=`pwd`/flutter/bin:`pwd`/flutter/bin/cache/dart-sdk/bin:$PATH
+    - ./script/incremental_build.sh build-examples --ipa
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 83d8345..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,112 +0,0 @@
-matrix:
-  include:
-    # Job 1) Run analyzer
-    - os: linux
-      env:
-        - SHARD=Analyze
-      sudo: false
-      addons:
-        apt:
-          # Flutter depends on /usr/lib/x86_64-linux-gnu/libstdc++.so.6 version GLIBCXX_3.4.18
-          sources:
-            - ubuntu-toolchain-r-test # if we don't specify this, the libstdc++6 we get is the wrong version
-          packages:
-            - libstdc++6
-            - fonts-droid
-      before_script:
-        - git clone https://github.com/flutter/flutter.git
-        - export PATH=`pwd`/flutter/bin:`pwd`/flutter/bin/cache/dart-sdk/bin:$PATH
-        - flutter doctor
-        - pub global activate flutter_plugin_tools
-      script:
-        - ./script/plugin_tools.sh analyze
-    # Job 2) Check format and run tests
-    - os: linux
-      env:
-        - SHARD=Format+Test
-      jdk: oraclejdk8
-      sudo: false
-      addons:
-        apt:
-          # Flutter depends on /usr/lib/x86_64-linux-gnu/libstdc++.so.6 version GLIBCXX_3.4.18
-          sources:
-            - ubuntu-toolchain-r-test # if we don't specify this, the libstdc++6 we get is the wrong version
-            - llvm-toolchain-precise # for clang-format-5.0
-          packages:
-            - libstdc++6
-            - fonts-droid
-      before_script:
-        - git clone https://github.com/flutter/flutter.git
-        - export PATH=`pwd`/flutter/bin:`pwd`/flutter/bin/cache/dart-sdk/bin:$PATH
-        - flutter doctor
-        - pub global activate flutter_plugin_tools
-      script:
-        - flutter format `pwd`/packages
-        - ./script/plugin_tools.sh test
-    # Job 3) Build example APKs and run Java tests
-    - os: linux
-      env:
-        - SHARD=Build-example-APKs+Java-Tests
-      jdk: oraclejdk8
-      sudo: false
-      addons:
-        apt:
-          # Flutter depends on /usr/lib/x86_64-linux-gnu/libstdc++.so.6 version GLIBCXX_3.4.18
-          sources:
-            - ubuntu-toolchain-r-test # if we don't specify this, the libstdc++6 we get is the wrong version
-          packages:
-            - lib32stdc++6 # https://github.com/flutter/flutter/issues/6207
-            - libstdc++6
-            - fonts-droid
-      before_script:
-        - wget https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip
-        - mkdir android-sdk
-        - unzip -qq sdk-tools-linux-3859397.zip -d android-sdk
-        - export ANDROID_HOME=`pwd`/android-sdk
-        - export PATH=`pwd`/android-sdk/tools/bin:$PATH
-        - mkdir -p /home/travis/.android # silence sdkmanager warning
-        - echo 'count=0' > /home/travis/.android/repositories.cfg # silence sdkmanager warning
-        # suppressing output of sdkmanager to keep log under 4MB (travis limit)
-        - echo y | sdkmanager "tools" >/dev/null
-        - echo y | sdkmanager "platform-tools" >/dev/null
-        - echo y | sdkmanager "build-tools;26.0.3" >/dev/null
-        - echo y | sdkmanager "platforms;android-26" >/dev/null
-        - echo y | sdkmanager "extras;android;m2repository" >/dev/null
-        - echo y | sdkmanager "extras;google;m2repository" >/dev/null
-        - echo y | sdkmanager "patcher;v4" >/dev/null
-        - sdkmanager --list
-        - wget http://services.gradle.org/distributions/gradle-4.1-bin.zip
-        - unzip -qq gradle-4.1-bin.zip
-        - export GRADLE_HOME=$PWD/gradle-4.1
-        - export PATH=$GRADLE_HOME/bin:$PATH
-        - gradle -v
-        - git clone https://github.com/flutter/flutter.git
-        - export PATH=`pwd`/flutter/bin:`pwd`/flutter/bin/cache/dart-sdk/bin:$PATH
-        - flutter doctor
-        - pub global activate flutter_plugin_tools
-      script:
-        - ./script/plugin_tools.sh build-examples --apk
-        - ./script/plugin_tools.sh java-test  # must come after apk build
-    # Job 4) Build example IPAs
-    - os: osx
-      env:
-        - SHARD=Build-example-IPAs
-      language: generic
-      osx_image: xcode9.3
-      before_script:
-        - brew update
-        - brew install libimobiledevice
-        - brew install ideviceinstaller
-        - brew install ios-deploy
-        - pod repo update
-        - gem update cocoapods
-        - git clone https://github.com/flutter/flutter.git
-        - export PATH=`pwd`/flutter/bin:`pwd`/flutter/bin/cache/dart-sdk/bin:$PATH
-        - flutter doctor
-        - pub global activate flutter_plugin_tools
-      script:
-        - ./script/plugin_tools.sh build-examples --ipa
-
-cache:
-  directories:
-    - $HOME/.pub-cache
diff --git a/packages/palette_generator/example/image_colors/lib/main.dart b/packages/palette_generator/example/image_colors/lib/main.dart
index c1ed3e0..0c90334 100644
--- a/packages/palette_generator/example/image_colors/lib/main.dart
+++ b/packages/palette_generator/example/image_colors/lib/main.dart
@@ -302,4 +302,3 @@
     return swatch;
   }
 }
-
diff --git a/packages/palette_generator/pubspec.yaml b/packages/palette_generator/pubspec.yaml
index 81234d1..400baac 100644
--- a/packages/palette_generator/pubspec.yaml
+++ b/packages/palette_generator/pubspec.yaml
@@ -11,7 +11,7 @@
   path: ^1.6.1
 
 dev_dependencies:
-  mockito: ^2.2.3
+  mockito: ^3.0.0
   flutter_test:
     sdk: flutter
 
diff --git a/packages/palette_generator/test/palette_generator_test.dart b/packages/palette_generator/test/palette_generator_test.dart
index c777b01..ae8021f 100644
--- a/packages/palette_generator/test/palette_generator_test.dart
+++ b/packages/palette_generator/test/palette_generator_test.dart
@@ -104,8 +104,10 @@
     final ImageProvider imageProvider = testImages['dominant'];
     Rect region = new Rect.fromLTRB(0.0, 0.0, 100.0, 100.0);
     const Size size = const Size(100.0, 100.0);
-    PaletteGenerator palette = await PaletteGenerator.fromImageProvider(imageProvider,
-        region: region, size: size);
+    PaletteGenerator palette = await PaletteGenerator.fromImageProvider(
+        imageProvider,
+        region: region,
+        size: size);
     expect(palette.paletteColors.length, equals(3));
     expect(palette.dominantColor.color,
         within<Color>(distance: 8, from: const Color(0xff0000ff)));
@@ -167,14 +169,15 @@
 
   test('PaletteGenerator limits max colors', () async {
     final ImageProvider imageProvider = testImages['landscape'];
-    PaletteGenerator palette =
-        await PaletteGenerator.fromImageProvider(imageProvider, maximumColorCount: 32);
+    PaletteGenerator palette = await PaletteGenerator.fromImageProvider(
+        imageProvider,
+        maximumColorCount: 32);
     expect(palette.paletteColors.length, equals(31));
-    palette =
-        await PaletteGenerator.fromImageProvider(imageProvider, maximumColorCount: 1);
+    palette = await PaletteGenerator.fromImageProvider(imageProvider,
+        maximumColorCount: 1);
     expect(palette.paletteColors.length, equals(1));
-    palette =
-        await PaletteGenerator.fromImageProvider(imageProvider, maximumColorCount: 15);
+    palette = await PaletteGenerator.fromImageProvider(imageProvider,
+        maximumColorCount: 15);
     expect(palette.paletteColors.length, equals(15));
   });
 
@@ -184,8 +187,9 @@
     List<PaletteFilter> filters = <PaletteFilter>[
       avoidRedBlackWhitePaletteFilter
     ];
-    PaletteGenerator palette =
-        await PaletteGenerator.fromImageProvider(imageProvider, filters: filters);
+    PaletteGenerator palette = await PaletteGenerator.fromImageProvider(
+        imageProvider,
+        filters: filters);
     final List<PaletteColor> expectedSwatches = <PaletteColor>[
       new PaletteColor(const Color(0xff3f630c), 10137),
       new PaletteColor(const Color(0xff3c4b2a), 4773),
@@ -213,7 +217,8 @@
 
     // A non-default filter works (and the default filter isn't applied too).
     filters = <PaletteFilter>[onlyBluePaletteFilter];
-    palette = await PaletteGenerator.fromImageProvider(imageProvider, filters: filters);
+    palette = await PaletteGenerator.fromImageProvider(imageProvider,
+        filters: filters);
     final List<PaletteColor> blueSwatches = <PaletteColor>[
       new PaletteColor(const Color(0xff4c5c75), 1515),
       new PaletteColor(const Color(0xff7483a1), 1505),
@@ -242,7 +247,8 @@
 
     // More than one filter is the intersection of the two filters.
     filters = <PaletteFilter>[onlyBluePaletteFilter, onlyCyanPaletteFilter];
-    palette = await PaletteGenerator.fromImageProvider(imageProvider, filters: filters);
+    palette = await PaletteGenerator.fromImageProvider(imageProvider,
+        filters: filters);
     final List<PaletteColor> blueGreenSwatches = <PaletteColor>[
       new PaletteColor(const Color(0xffc8e8f8), 87),
       new PaletteColor(const Color(0xff5c6c74), 73),
@@ -271,7 +277,8 @@
 
     // Mutually exclusive filters return an empty palette.
     filters = <PaletteFilter>[onlyBluePaletteFilter, onlyGreenPaletteFilter];
-    palette = await PaletteGenerator.fromImageProvider(imageProvider, filters: filters);
+    palette = await PaletteGenerator.fromImageProvider(imageProvider,
+        filters: filters);
     expect(palette.paletteColors, isEmpty);
     expect(palette.dominantColor, isNull);
     expect(palette.colors, isEmpty);
@@ -281,7 +288,8 @@
     final ImageProvider imageProvider = testImages['landscape'];
     // Passing an empty set of targets works the same as passing a null targets
     // list.
-    PaletteGenerator palette = await PaletteGenerator.fromImageProvider(imageProvider,
+    PaletteGenerator palette = await PaletteGenerator.fromImageProvider(
+        imageProvider,
         targets: <PaletteTarget>[]);
     expect(palette.selectedSwatches, isNotEmpty);
     expect(palette.vibrantColor, isNotNull);
diff --git a/script/check_publish.sh b/script/check_publish.sh
new file mode 100755
index 0000000..c12b776
--- /dev/null
+++ b/script/check_publish.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+set -e
+
+# This script checks to make sure that each of the plugins *could* be published.
+# It doesn't actually publish anything.
+
+# So that users can run this script from anywhere and it will work as expected.
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
+REPO_DIR="$(dirname "$SCRIPT_DIR")"
+
+source "$SCRIPT_DIR/common.sh"
+
+function check_publish() {
+  local failures=()
+  for package_name in "$@"; do
+    local dir="$REPO_DIR/packages/$package_name"
+    echo "Checking that $package_name can be published."
+    if (cd "$dir" && pub publish --dry-run > /dev/null); then
+      echo "Package $package_name is able to be published."
+    else
+      error "Unable to publish $package_name"
+      failures=("${failures[@]}" "$package_name")
+    fi
+  done
+  if [[ "${#failures[@]}" != 0 ]]; then
+    error "FAIL: The following ${#failures[@]} package(s) failed the publishing check:"
+    for failure in "${failures[@]}"; do
+      error "$failure"
+    done
+  fi
+  return "${#failures[@]}"
+}
+
+# Sets CHANGED_PACKAGE_LIST
+check_changed_packages
+
+if [[ "${#CHANGED_PACKAGE_LIST[@]}" != 0 ]]; then
+  check_publish "${CHANGED_PACKAGE_LIST[@]}"
+fi
\ No newline at end of file
diff --git a/script/common.sh b/script/common.sh
new file mode 100644
index 0000000..eaf94c7
--- /dev/null
+++ b/script/common.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+
+function error() {
+  echo "$@" 1>&2
+}
+
+function check_changed_packages() {
+  # Try get a merge base for the branch and calculate affected packages.
+  # We need this check because some CIs can do a single branch clones with a limited history of commits.
+  local packages
+  local branch_base_sha="$(git merge-base --fork-point FETCH_HEAD HEAD || git merge-base FETCH_HEAD HEAD)"
+  if [[ "$?" == 0 ]]; then
+    echo "Checking for changed packages from $branch_base_sha"
+    IFS=$'\n' packages=( $(git diff --name-only "$branch_base_sha" HEAD | grep -o "packages/[^/]*" | sed -e "s/packages\///g" | sort | uniq) )
+  else
+    error "Cannot find a merge base for the current branch to run an incremental build..."
+    error "Please rebase your branch onto the latest master!"
+    return 1
+  fi
+
+  # Filter out any packages that don't have a pubspec.yaml: they have probably
+  # been deleted in this PR.
+  CHANGED_PACKAGES=""
+  CHANGED_PACKAGE_LIST=()
+  for package in "${packages[@]}"; do
+    if [[ -f "$REPO_DIR/packages/$package/pubspec.yaml" ]]; then
+      CHANGED_PACKAGES="${CHANGED_PACKAGES},$package"
+      CHANGED_PACKAGE_LIST=("${CHANGED_PACKAGE_LIST[@]}" "$package")
+    fi
+  done
+
+  if [[ "${#CHANGED_PACKAGE_LIST[@]}" == 0 ]]; then
+    echo "No changes detected in packages."
+  else
+    echo "Detected changes in the following ${#CHANGED_PACKAGE_LIST[@]} package(s):"
+    for package in "${CHANGED_PACKAGE_LIST[@]}"; do
+      echo "$package"
+    done
+    echo ""
+  fi
+  return 0
+}
diff --git a/script/incremental_build.sh b/script/incremental_build.sh
index d2d2243..c3a3941 100755
--- a/script/incremental_build.sh
+++ b/script/incremental_build.sh
@@ -1,35 +1,29 @@
 #!/bin/bash
+set -e
 
-set -ev
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
+REPO_DIR="$(dirname "$SCRIPT_DIR")"
 
-BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)
+source "$SCRIPT_DIR/common.sh"
 
-if [ "${BRANCH_NAME}" = "master" ]; then
+# Set some default actions if run without arguments.
+ACTIONS=("$@")
+if [[ "${#ACTIONS[@]}" == 0 ]]; then
+  ACTIONS=("test" "analyze" "java-test")
+fi
+
+BRANCH_NAME="${BRANCH_NAME:-"$(git rev-parse --abbrev-ref HEAD)"}"
+if [[ "${BRANCH_NAME}" == "master" ]]; then
   echo "Running for all packages"
-  pub global run flutter_plugin_tools "$@"
+  (cd "$REPO_DIR" && pub global run flutter_plugin_tools "${ACTIONS[@]}" $PLUGIN_SHARDING)
 else
-  # Make sure there is up-to-date master.
-  git fetch origin master
+  # Sets CHANGED_PACKAGES
+  check_changed_packages
 
-  FLUTTER_CHANGED_GLOBAL=0
-  FLUTTER_CHANGED_PACKAGES=""
-
-  # Try get a merge base for the branch and calculate affected packages.
-  # We need this check because some CIs can do a single branch clones with a limited history of commits.
-  if BRANCH_BASE_SHA=$(git merge-base --fork-point FETCH_HEAD HEAD); then
-    echo "Checking changes from $BRANCH_BASE_SHA..."
-    FLUTTER_CHANGED_GLOBAL=`git diff --name-only $BRANCH_BASE_SHA HEAD | grep -v packages | wc -l`
-    FLUTTER_CHANGED_PACKAGES=`git diff --name-only $BRANCH_BASE_SHA HEAD | grep -o "packages/[^/]*" | sed -e "s/packages\///g" | sort | uniq | paste -s -d, -`
-  else
-    echo "Cannot find a merge base for the current branch to run an incremental build..."
-    echo "Please rebase your branch onto the latest master!"
-  fi
-
-  if [ "${FLUTTER_CHANGED_PACKAGES}" = "" ] || [ $FLUTTER_CHANGED_GLOBAL -gt 0 ]; then
+  if [[ "$CHANGED_PACKAGES" == "" ]]; then
     echo "Running for all packages"
-    pub global run flutter_plugin_tools "$@"
+    (cd "$REPO_DIR" && pub global run flutter_plugin_tools "${ACTIONS[@]}" $PLUGIN_SHARDING)
   else
-    echo "Running only for $FLUTTER_CHANGED_PACKAGES"
-    pub global run flutter_plugin_tools "$@" --plugins=$FLUTTER_CHANGED_PACKAGES
+    (cd "$REPO_DIR" && pub global run flutter_plugin_tools "${ACTIONS[@]}" --plugins="$CHANGED_PACKAGES" $PLUGIN_SHARDING)
   fi
 fi
diff --git a/script/plugin_tools.sh b/script/plugin_tools.sh
deleted file mode 100755
index 516ba01..0000000
--- a/script/plugin_tools.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-set -ev
-
-if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then
-  echo "Running for all packages"
-  pub global run flutter_plugin_tools "$@"
-else
-  echo "Looking for changes in $TRAVIS_COMMIT_RANGE"
-  FLUTTER_CHANGED_GLOBAL=`git diff --name-only $TRAVIS_COMMIT_RANGE | grep -v packages | wc -l`
-  FLUTTER_CHANGED_PACKAGES=`git diff --name-only $TRAVIS_COMMIT_RANGE | grep -o "packages/[^/]*" | sed -e "s/packages\///g" | sort | uniq | paste -s -d, -`
-  if [ "${FLUTTER_CHANGED_PACKAGES}" = "" ] || [ $FLUTTER_CHANGED_GLOBAL -gt 0 ]; then
-    echo "Running for all packages"
-    pub global run flutter_plugin_tools "$@"
-  else
-    echo "Running only for $FLUTTER_CHANGED_PACKAGES"
-    pub global run flutter_plugin_tools "$@" --plugins=$FLUTTER_CHANGED_PACKAGES
-  fi
-fi