misc changes to dart-services
diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml
index a621cdb..095b6c7 100644
--- a/.github/workflows/dart.yml
+++ b/.github/workflows/dart.yml
@@ -11,7 +11,7 @@
     - cron: "0 0 * * *" # Every day at midnight
 
 jobs:
-  # Check code formating, static analysis, and build on a single OS (linux)
+  # Check code formatting, static analysis, and build on a single OS (linux)
   # against Dart stable and beta.
   analyze:
     runs-on: ubuntu-latest
@@ -30,14 +30,14 @@
         run: dart --version
       - name: Install dependencies
         run: dart pub get
-      - name: Analyze code (lib and test)
-        run: dart analyze --fatal-infos .
-      - name: Analyze code (example)
+      - name: Analyze code (example/)
         run: |
           cd example
           echo [Analyzing example]
           dart pub get
           dart analyze --fatal-infos .
+      - name: Analyze code
+        run: dart analyze --fatal-infos .
       - name: Prepare Flutter
         run: |
           git submodule init
diff --git a/.gitignore b/.gitignore
index 76d99c4..c15e696 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@
 # Other files.
 coverage.json
 lcov.info
+.idea/
 
 # Redis snapshot persistance file
 dump.rdb
diff --git a/.gitpod.yml b/.gitpod.yml
index 9591a59..6205d4d 100644
--- a/.gitpod.yml
+++ b/.gitpod.yml
@@ -23,4 +23,4 @@
 # Add the DartCode extension. See https://www.gitpod.io/docs/vscode-extensions/
 vscode:
   extensions:
-    - Dart-Code.dart-code@3.9.0:umvoqCQMs3NpksmwhVtStw==
\ No newline at end of file
+    - Dart-Code.dart-code@3.9.0:umvoqCQMs3NpksmwhVtStw==
diff --git a/Dockerfile b/Dockerfile
index f1a493f..bb00126 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-# Keep aligned with min SDK in pubspec.yaml and Dart test version in .travis.yml
+# Keep aligned with min SDK in pubspec.yaml and .github/workflows/dart.yml.
 FROM google/dart:2.10.4
 
 # The specific commit that dart-services should use. This should be kept
diff --git a/analysis_options.yaml b/analysis_options.yaml
index a812034..06787ce 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -4,11 +4,11 @@
   strong-mode:
     implicit-casts: false
   exclude:
-    - "dart-sdk/**"
-    - "third_party/**"
-    - "doc/generated/**"
-    - "flutter/**"
-    - "lib/src/protos/**"
+    - dart-sdk/**
+    - doc/generated/**
+    - flutter/**
+    # TODO: This seems to be hiding ~2 dozen legitimate analysis issues.
+    - lib/src/protos/**
 
 linter:
   rules:
@@ -17,3 +17,4 @@
     - prefer_final_in_for_each
     - prefer_final_locals
     - prefer_relative_imports
+    - sort_pub_dependencies
diff --git a/lib/services_gae.dart b/lib/services_gae.dart
index 3f6591b..8c234c5 100644
--- a/lib/services_gae.dart
+++ b/lib/services_gae.dart
@@ -20,6 +20,7 @@
 const String _API_PREFIX = '/api/dartservices/';
 const String _livenessCheck = '/liveness_check';
 const String _readinessCheck = '/readiness_check';
+
 // Serve content for 1.5 hours, +- 30 minutes.
 final DateTime _serveUntil = DateTime.now()
     .add(Duration(hours: 1))
diff --git a/lib/src/compiler.dart b/lib/src/compiler.dart
index 061a78a..48d91ef 100644
--- a/lib/src/compiler.dart
+++ b/lib/src/compiler.dart
@@ -9,7 +9,6 @@
 import 'dart:io';
 
 import 'package:bazel_worker/driver.dart';
-import 'package:io/io.dart';
 import 'package:logging/logging.dart';
 import 'package:path/path.dart' as path;
 
@@ -263,3 +262,39 @@
   @override
   String toString() => message;
 }
+
+/// Copies all of the files in the [from] directory to [to].
+///
+/// This is similar to `cp -R <from> <to>`:
+/// * Symlinks are supported.
+/// * Existing files are over-written, if any.
+/// * If [to] is within [from], throws [ArgumentError] (an infinite operation).
+/// * If [from] and [to] are canonically the same, no operation occurs.
+///
+/// Returns a future that completes when complete.
+Future<Null> copyPath(String from, String to) async {
+  if (_doNothing(from, to)) {
+    return;
+  }
+  await Directory(to).create(recursive: true);
+  await for (final file in Directory(from).list(recursive: true)) {
+    final copyTo = path.join(to, path.relative(file.path, from: from));
+    if (file is Directory) {
+      await Directory(copyTo).create(recursive: true);
+    } else if (file is File) {
+      await File(file.path).copy(copyTo);
+    } else if (file is Link) {
+      await Link(copyTo).create(await file.target(), recursive: true);
+    }
+  }
+}
+
+bool _doNothing(String from, String to) {
+  if (path.canonicalize(from) == path.canonicalize(to)) {
+    return true;
+  }
+  if (path.isWithin(from, to)) {
+    throw ArgumentError('Cannot copy from $from to $to');
+  }
+  return false;
+}
diff --git a/lib/src/server_cache.dart b/lib/src/server_cache.dart
index 538c71a..198cc1c 100644
--- a/lib/src/server_cache.dart
+++ b/lib/src/server_cache.dart
@@ -215,6 +215,7 @@
 /// An in-memory implementation of [ServerCache] which doesn't support
 /// expiration of entries based on time.
 class InMemoryCache implements ServerCache {
+  // TODO: This is the only use of package:quiver; consider in-lining it.
   /// Wrapping an internal cache with a maximum size of 512 entries.
   final Cache<String, String> _lru =
       MapCache<String, String>.lru(maximumSize: 512);
diff --git a/pubspec.yaml b/pubspec.yaml
index 5ae5ba9..6b389f0 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -2,19 +2,18 @@
 publish_to: none
 
 environment:
-  # Keep SDK aligned with .travis.yml and FROM line of Dockerfile
+  # Keep this aligned with .github/workflows/dart.yml and the FROM line of Dockerfile
   sdk: ^2.10.4
 
 dependencies:
-  analyzer: ^0.40.6
   analysis_server_lib: ^0.1.4
+  analyzer: ^0.40.6
   appengine: ^0.12.0
   args: ^1.6.0
   bazel_worker: ^0.1.23
   crypto: ^2.0.0
   dartis: ^0.5.0
   http: ^0.12.0
-  io: ^0.3.4
   logging: ^0.11.0
   meta: ^1.1.8
   path: ^1.6.2
@@ -22,8 +21,6 @@
   quiver: ^2.0.3
   shelf: ^0.7.5
   shelf_router: ^0.7.0+1
-  uuid: ^2.0.0
-  yaml: ^2.1.15
 
 dev_dependencies:
   async: ^2.2.0
@@ -33,10 +30,8 @@
   grinder: ^0.8.0
   mock_request: ^1.0.7
   pedantic: ^1.9.0
-  shelf_router_generator: ^0.7.0+1
   synchronized: ^2.1.0
   test: ^1.6.4
-  tuneup: ^0.3.6
 
 executables:
   services: null