Add check for API leaks to the Health workflow (#251)

* Add checks for API leaks

* Fixes

* More fixes

* Fix SDK version in testdata

* Install dart_apitool on leaking in action

* Update tests

* Fix message

* Add exports

* Fix analyze issues

* Reset tests after adding a period
diff --git a/.github/workflows/health.yaml b/.github/workflows/health.yaml
index 74b0b00..db945f0 100644
--- a/.github/workflows/health.yaml
+++ b/.github/workflows/health.yaml
@@ -21,9 +21,9 @@
 #     uses: dart-lang/ecosystem/.github/workflows/health.yaml@main
 #     with:
 #       sdk: beta
-#       checks: "version,changelog,license,coverage,breaking,do-not-submit"
+#       checks: "version,changelog,license,coverage,breaking,do-not-submit,leaking"
 #       fail_on: "version,changelog,do-not-submit"
-#       warn_on: "license,coverage,breaking"
+#       warn_on: "license,coverage,breaking,leaking"
 #       coverage_web: false
 #       upload_coverage: false
 #       use-flutter: true
@@ -54,18 +54,18 @@
       # Restrict the checks to any subset of version, changelog, and license if
       # needed.
       checks:
-        description: What to check for in the PR health check - any subset of "version,changelog,license,coverage,breaking,do-not-submit"
-        default: "version,changelog,license,coverage,breaking,do-not-submit"
+        description: What to check for in the PR health check - any subset of "version,changelog,license,coverage,breaking,do-not-submit,leaking"
+        default: "version,changelog,license,coverage,breaking,do-not-submit,leaking"
         type: string
         required: false
       fail_on:
-        description: Which checks should lead to failure - any subset of "version,changelog,license,coverage,breaking,do-not-submit"
+        description: Which checks should lead to failure - any subset of "version,changelog,license,coverage,breaking,do-not-submit,leaking"
         default: "version,changelog,do-not-submit"
         type: string
         required: false
       warn_on:
-        description: Which checks should not fail, but only warn - any subset of "version,changelog,license,coverage,breaking,do-not-submit"
-        default: "license,coverage,breaking"
+        description: Which checks should not fail, but only warn - any subset of "version,changelog,license,coverage,breaking,do-not-submit,leaking"
+        default: "license,coverage,breaking,leaking"
         type: string
         required: false
       local_debug:
@@ -198,8 +198,21 @@
       ignore_packages: ${{ inputs.ignore_packages }}
       checkout_submodules: ${{ inputs.checkout_submodules }}
 
+  leaking:
+    if: ${{ contains(inputs.checks, 'leaking') }}
+    uses: ./.github/workflows/health_base.yaml
+    with:
+      sdk: ${{ inputs.sdk }}
+      check: leaking
+      fail_on: ${{ inputs.fail_on }}
+      warn_on: ${{ inputs.warn_on }}
+      local_debug: ${{ inputs.local_debug }}
+      use-flutter: ${{ inputs.use-flutter }}
+      ignore_packages: ${{ inputs.ignore_packages }}
+      checkout_submodules: ${{ inputs.checkout_submodules }}
+
   comment:
-    needs: [version, changelog, license, coverage, breaking, do-not-submit]
+    needs: [version, changelog, license, coverage, breaking, do-not-submit, leaking]
     if: always()
     # These permissions are required for us to create comments on PRs.
     permissions:
diff --git a/.github/workflows/health_base.yaml b/.github/workflows/health_base.yaml
index 6ae942c..fdf1f48 100644
--- a/.github/workflows/health_base.yaml
+++ b/.github/workflows/health_base.yaml
@@ -16,17 +16,17 @@
         required: false
         type: string
       check:
-        description: What to check for in the PR health check - any of "version,changelog,license,coverage,breaking,do-not-submit"
+        description: What to check for in the PR health check - any of "version,changelog,license,coverage,breaking,do-not-submit,leaking"
         type: string
         required: true
       fail_on:
-        description: Which checks should lead to failure - any subset of "version,changelog,license,coverage,breaking,do-not-submit"
+        description: Which checks should lead to failure - any subset of "version,changelog,license,coverage,breaking,do-not-submit,leaking"
         default: "version,changelog,do-not-submit"
         type: string
         required: false
       warn_on:
-        description: Which checks should not fail, but only warn - any subset of "version,changelog,license,coverage,breaking,do-not-submit"
-        default: "license,coverage,breaking"
+        description: Which checks should not fail, but only warn - any subset of "version,changelog,license,coverage,breaking,do-not-submit,leaking"
+        default: "license,coverage,breaking,leaking"
         type: string
         required: false
       local_debug:
@@ -123,7 +123,7 @@
         
       - name: Install api_tool
         run: dart pub global activate dart_apitool
-        if: ${{ inputs.check == 'breaking' }}
+        if: ${{ inputs.check == 'breaking' || inputs.check == 'leaking' }}
       
       - name: Check PR health
         id: healthstep
diff --git a/.github/workflows/health_internal.yaml b/.github/workflows/health_internal.yaml
index 270a0b8..26ed3ce 100644
--- a/.github/workflows/health_internal.yaml
+++ b/.github/workflows/health_internal.yaml
@@ -12,8 +12,8 @@
       local_debug: true
       coverage_web: false
       upload_coverage: false
-      checks: version,changelog,license,coverage,breaking,do-not-submit
+      checks: version,changelog,license,coverage,breaking,do-not-submit,leaking
       fail_on: version,changelog,do-not-submit
-      warn_on: license,coverage,breaking
+      warn_on: license,coverage,breaking,leaking
       ignore_license: 'pkgs/firehose/test_data'
-      ignore_coverage: 'pkgs/firehose/bin'
+      ignore_coverage: 'pkgs/firehose/bin,pkgs/firehose/test_data'
diff --git a/.vscode/launch.json b/.vscode/launch.json
index b76d2fd..cfe3049 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -17,7 +17,7 @@
             },
             "args": [
                 "--checks",
-                "version,changelog,license,coverage,do-not-submit"
+                "version,changelog,license,coverage,do-not-submit,leaking"
             ]
         }
     ]
diff --git a/pkgs/firehose/CHANGELOG.md b/pkgs/firehose/CHANGELOG.md
index 9711bd9..2e49ae3 100644
--- a/pkgs/firehose/CHANGELOG.md
+++ b/pkgs/firehose/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.9.0
+
+- Add `leaking` check to the health workflow.
+
 ## 0.8.0
 
 - Only check text files for do not submit strings.
diff --git a/pkgs/firehose/README.md b/pkgs/firehose/README.md
index 30d7abd..9ea55a5 100644
--- a/pkgs/firehose/README.md
+++ b/pkgs/firehose/README.md
@@ -166,6 +166,7 @@
 * How the test coverage is affected by the PR.
 * The package versioning takes into account any breaking changes in the PR.
 * The PR contains `DO_NOT_SUBMIT` strings in the files or the description.
+* Any symbols are visible in the public API, but not exported.
 
 This tool can work with either single package repos or with mono-repos (repos
 containing several packages).
@@ -192,9 +193,9 @@
 
 | Name | Type | Description | Example |
 | ------------- | ------------- | ------------- | ------------- |
-| checks  | List of strings  | What to check for in the PR health check | `"version,changelog,license,coverage,breaking,do-not-submit"` |
+| checks  | List of strings  | What to check for in the PR health check | `"version,changelog,license,coverage,breaking,do-not-submit,leaking"` |
 | fail_on  | List of strings  | Which checks should lead to failure | `"version,changelog,do-not-submit"` |
-| warn_on  | List of strings  | Which checks should not fail, but only warn | `"license,coverage,breaking"` |
+| warn_on  | List of strings  | Which checks should not fail, but only warn | `"license,coverage,breaking,leaking"` |
 | upload_coverage  | boolean  | Whether to upload the coverage to [coveralls](https://coveralls.io/) | `true` |
 | coverage_web  | boolean  | Whether to run `dart test -p chrome` for coverage | `false` |
 | use-flutter  | boolean  | Whether to setup Flutter in this workflow | `false` |
diff --git a/pkgs/firehose/bin/firehose.dart b/pkgs/firehose/bin/firehose.dart
index 65d0000..82bb7dd 100644
--- a/pkgs/firehose/bin/firehose.dart
+++ b/pkgs/firehose/bin/firehose.dart
@@ -6,7 +6,6 @@
 
 import 'package:args/args.dart';
 import 'package:firehose/firehose.dart';
-import 'package:firehose/src/github.dart';
 import 'package:glob/glob.dart';
 
 const helpFlag = 'help';
diff --git a/pkgs/firehose/bin/health.dart b/pkgs/firehose/bin/health.dart
index 5a36b21..697dd74 100644
--- a/pkgs/firehose/bin/health.dart
+++ b/pkgs/firehose/bin/health.dart
@@ -9,6 +9,7 @@
 import 'package:firehose/src/health/health.dart';
 
 void main(List<String> arguments) async {
+  var checkTypes = Check.values.map((c) => c.name);
   var argParser = ArgParser()
     ..addOption(
       'check',
@@ -49,7 +50,8 @@
       help: 'Whether to run web tests for coverage',
     );
   final parsedArgs = argParser.parse(arguments);
-  final check = parsedArgs['check'] as String;
+  final checkStr = parsedArgs['check'] as String;
+  final check = Check.values.firstWhere((c) => c.name == checkStr);
   final warnOn = parsedArgs['warn_on'] as List<String>;
   final failOn = parsedArgs['fail_on'] as List<String>;
   final ignorePackages = _listNonEmpty(parsedArgs, 'ignore_packages');
diff --git a/pkgs/firehose/lib/firehose.dart b/pkgs/firehose/lib/firehose.dart
index 5c702f6..618dbb6 100644
--- a/pkgs/firehose/lib/firehose.dart
+++ b/pkgs/firehose/lib/firehose.dart
@@ -14,6 +14,11 @@
 import 'src/repo.dart';
 import 'src/utils.dart';
 
+export 'src/changelog.dart' show Changelog;
+export 'src/github.dart' show FileStatus, GitFile, GithubApi;
+export 'src/repo.dart' show Package, Repository;
+export 'src/utils.dart' show Severity;
+
 const String _botSuffix = '[bot]';
 
 const String _githubActionsUser = 'github-actions[bot]';
diff --git a/pkgs/firehose/lib/src/health/health.dart b/pkgs/firehose/lib/src/health/health.dart
index dc9343d..e0cc3b0 100644
--- a/pkgs/firehose/lib/src/health/health.dart
+++ b/pkgs/firehose/lib/src/health/health.dart
@@ -14,33 +14,26 @@
 import 'package:pub_semver/pub_semver.dart';
 
 import '../../firehose.dart';
-import '../github.dart';
-import '../repo.dart';
 import '../utils.dart';
 import 'changelog.dart';
 import 'coverage.dart';
 import 'license.dart';
 
-const String _publishBotTag2 = '### Package publish validation';
+enum Check {
+  version('### Package publish validation', 'version'),
+  license('### License Headers', 'license'),
+  changelog('### Changelog Entry', 'changelog'),
+  coverage('### Coverage', 'coverage'),
+  breaking('### Breaking changes', 'breaking'),
+  leaking('### API leaks', 'leaking'),
+  donotsubmit('### Do Not Submit', 'do-not-submit');
 
-const String _licenseBotTag = '### License Headers';
+  final String tag;
 
-const String _changelogBotTag = '### Changelog Entry';
+  final String name;
 
-const String _doNotSubmitBotTag = '### Do Not Submit';
-
-const String _coverageBotTag = '### Coverage';
-
-const String _breakingBotTag = '### Breaking changes';
-
-const checkTypes = <String>[
-  'version',
-  'license',
-  'changelog',
-  'coverage',
-  'breaking',
-  'do-not-submit',
-];
+  const Check(this.tag, this.name);
+}
 
 class Health {
   final Directory directory;
@@ -78,7 +71,7 @@
             );
   final GithubApi github;
 
-  final String check;
+  final Check check;
   final List<String> warnOn;
   final List<String> failOn;
   final bool coverageweb;
@@ -94,7 +87,8 @@
     if (!expectEnv(github.issueNumber?.toString(), 'ISSUE_NUMBER')) return;
     if (!expectEnv(github.sha, 'GITHUB_SHA')) return;
 
-    print('Start health check for the check $check with');
+    var checkName = check.name;
+    print('Start health check for the check $checkName with');
     print(' warnOn: $warnOn');
     print(' failOn: $failOn');
     print(' coverageweb: $coverageweb');
@@ -103,44 +97,35 @@
     print(' ignoredForCoverage: $ignoredFilesForCoverage');
     print(' baseDirectory: $baseDirectory');
     print(' experiments: $experiments');
-    print('Checking for $check');
-    if (!github.prLabels.contains('skip-$check-check')) {
+    print('Checking for $checkName');
+    if (!github.prLabels.contains('skip-$checkName-check')) {
       final firstResult = await checkFor(check)();
       final HealthCheckResult finalResult;
-      if (warnOn.contains(check) && firstResult.severity == Severity.error) {
+      if (warnOn.contains(check.name) &&
+          firstResult.severity == Severity.error) {
         finalResult = firstResult.withSeverity(Severity.warning);
-      } else if (failOn.contains(check) &&
+      } else if (failOn.contains(check.name) &&
           firstResult.severity == Severity.warning) {
         finalResult = firstResult.withSeverity(Severity.error);
       } else {
         finalResult = firstResult;
       }
       await writeInComment(github, finalResult);
-      print('\n\n${finalResult.severity.name.toUpperCase()}: $check done.\n\n');
+      var severity = finalResult.severity.name.toUpperCase();
+      print('\n\n$severity: $checkName done.\n\n');
     } else {
-      print('Skipping $check, as the skip tag is present.');
+      print('Skipping $checkName, as the skip tag is present.');
     }
   }
 
-  String tagFor(String checkType) => switch (checkType) {
-        'version' => _publishBotTag2,
-        'license' => _licenseBotTag,
-        'changelog' => _changelogBotTag,
-        'coverage' => _coverageBotTag,
-        'breaking' => _breakingBotTag,
-        'do-not-submit' => _doNotSubmitBotTag,
-        String() => throw ArgumentError('Invalid check type $checkType'),
-      };
-
-  Future<HealthCheckResult> Function() checkFor(String checkType) =>
-      switch (checkType) {
-        'version' => validateCheck,
-        'license' => licenseCheck,
-        'changelog' => changelogCheck,
-        'coverage' => coverageCheck,
-        'breaking' => breakingCheck,
-        'do-not-submit' => doNotSubmitCheck,
-        String() => throw ArgumentError('Invalid check type $checkType'),
+  Future<HealthCheckResult> Function() checkFor(Check check) => switch (check) {
+        Check.version => validateCheck,
+        Check.license => licenseCheck,
+        Check.changelog => changelogCheck,
+        Check.coverage => coverageCheck,
+        Check.breaking => breakingCheck,
+        Check.donotsubmit => doNotSubmitCheck,
+        Check.leaking => leakingCheck,
       };
 
   Future<HealthCheckResult> validateCheck() async {
@@ -157,7 +142,7 @@
     ''';
 
     return HealthCheckResult(
-      'version',
+      Check.version,
       results.severity,
       markdownTable,
     );
@@ -173,7 +158,7 @@
       var baseRelativePath = path.relative(
           path.join(baseDirectory.path, relativePath),
           from: directory.path);
-      var tempDirectory = Directory.systemTemp..createSync();
+      var tempDirectory = Directory.systemTemp.createTempSync();
       var reportPath = path.join(tempDirectory.path, 'report.json');
       var runApiTool = Process.runSync(
         'dart',
@@ -209,7 +194,7 @@
       );
     }
     return HealthCheckResult(
-      'breaking',
+      Check.breaking,
       changeForPackage.values.any((element) => !element.versionIsFine)
           ? Severity.warning
           : Severity.info,
@@ -235,6 +220,54 @@
     return breakingLevel;
   }
 
+  Future<HealthCheckResult> leakingCheck() async {
+    final filesInPR = await github.listFilesForPR(directory, ignoredPackages);
+    final leaksForPackage = <Package, List<String>>{};
+    for (var package in packagesContaining(filesInPR, ignoredPackages)) {
+      print('Look for leaks in $package');
+      var relativePath =
+          path.relative(package.directory.path, from: directory.path);
+      var tempDirectory = Directory.systemTemp.createTempSync();
+      var reportPath = path.join(tempDirectory.path, 'leaks.json');
+      var runApiTool = Process.runSync(
+        'dart',
+        [
+          ...['pub', 'global', 'run'],
+          'dart_apitool:main',
+          'extract',
+          ...['--input', relativePath],
+          ...['--output', reportPath],
+          '--set-exit-on-missing-export',
+        ],
+        workingDirectory: directory.path,
+      );
+      print(runApiTool.stderr);
+      print(runApiTool.stdout);
+
+      var fullReportString = File(reportPath).readAsStringSync();
+      var decoded = jsonDecode(fullReportString) as Map<String, dynamic>;
+      var leaks = decoded['missingEntryPoints'] as List<dynamic>;
+
+      print('Leaking symbols in API:\n$leaks');
+      if (leaks.isNotEmpty) {
+        leaksForPackage[package] = leaks.cast();
+      }
+    }
+    return HealthCheckResult(
+      Check.leaking,
+      leaksForPackage.values.any((leaks) => leaks.isNotEmpty)
+          ? Severity.warning
+          : Severity.success,
+      '''
+The following packages contain symbols visible in the public API, but not exported by the library. Export these symbols or remove them from your publicly visible API.
+
+| Package | Leaked API symbols |
+| :--- | :--- |
+${leaksForPackage.entries.map((e) => '|${e.key.name}|${e.value.join('<br>')}|').join('\n')}
+''',
+    );
+  }
+
   Future<HealthCheckResult> licenseCheck() async {
     var files = await github.listFilesForPR(directory, ignoredPackages);
     var allFilePaths = await getFilesWithoutLicenses(
@@ -276,7 +309,7 @@
 ''';
 
     return HealthCheckResult(
-      'license',
+      Check.license,
       changedFilesPaths.isNotEmpty ? Severity.error : Severity.success,
       markdownResult,
     );
@@ -298,7 +331,7 @@
 ''';
 
     return HealthCheckResult(
-      'changelog',
+      Check.changelog,
       filePaths.isNotEmpty ? Severity.error : Severity.success,
       markdownResult,
     );
@@ -335,7 +368,7 @@
 
     final hasDNS = filesWithDNS.isNotEmpty || bodyContainsDNS;
     return HealthCheckResult(
-      'do-not-submit',
+      Check.donotsubmit,
       hasDNS ? Severity.error : Severity.success,
       hasDNS ? markdownResult : null,
     );
@@ -359,7 +392,7 @@
 ''';
 
     return HealthCheckResult(
-      'coverage',
+      Check.coverage,
       Severity.values[coverage.coveragePerFile.values
           .map((change) => change.severity.index)
           .fold(0, max)],
@@ -381,11 +414,11 @@
 
 $markdown
 
-${isWorseThanInfo ? 'This check can be disabled by tagging the PR with `skip-${result.name}-check`' : ''}
+${isWorseThanInfo ? 'This check can be disabled by tagging the PR with `skip-${result.check.name}-check`.' : ''}
 </details>
 
 ''';
-      markdownSummary = '${tagFor(result.name)} ${result.severity.emoji}\n\n$s';
+      markdownSummary = '${check.tag} ${result.severity.emoji}\n\n$s';
     } else {
       markdownSummary = '';
     }
@@ -426,14 +459,14 @@
 }
 
 class HealthCheckResult {
-  final String name;
+  final Check check;
   final Severity severity;
   final String? markdown;
 
-  HealthCheckResult(this.name, this.severity, this.markdown);
+  HealthCheckResult(this.check, this.severity, this.markdown);
 
   HealthCheckResult withSeverity(Severity severity) => HealthCheckResult(
-        name,
+        check,
         severity,
         markdown,
       );
diff --git a/pkgs/firehose/pubspec.yaml b/pkgs/firehose/pubspec.yaml
index 89be06a..e7ec8bd 100644
--- a/pkgs/firehose/pubspec.yaml
+++ b/pkgs/firehose/pubspec.yaml
@@ -1,6 +1,6 @@
 name: firehose
 description: A tool to automate publishing of Pub packages from GitHub actions.
-version: 0.8.0
+version: 0.9.0
 repository: https://github.com/dart-lang/ecosystem/tree/main/pkgs/firehose
 
 environment:
diff --git a/pkgs/firehose/test/health_test.dart b/pkgs/firehose/test/health_test.dart
index 41f408e..8ab040d 100644
--- a/pkgs/firehose/test/health_test.dart
+++ b/pkgs/firehose/test/health_test.dart
@@ -30,13 +30,23 @@
       FileStatus.added,
       directory,
     ),
+    GitFile(
+      'pkgs/package5/lib/src/package5_base.dart',
+      FileStatus.modified,
+      directory,
+    ),
+    GitFile(
+      'pkgs/package5/pubspec.yaml',
+      FileStatus.modified,
+      directory,
+    ),
   ]);
   await Process.run('dart', ['pub', 'global', 'activate', 'dart_apitool']);
   await Process.run('dart', ['pub', 'global', 'activate', 'coverage']);
 
-  for (var check in checkTypes) {
+  for (var check in Check.values) {
     test(
-      'Check health workflow "$check" against golden files',
+      'Check health workflow "${check.name}" against golden files',
       () async => await checkGolden(check, fakeGithubApi, directory),
       timeout: const Timeout(Duration(minutes: 2)),
     );
@@ -44,7 +54,7 @@
 
   test('Ignore license test', () async {
     await checkGolden(
-      'license',
+      Check.license,
       fakeGithubApi,
       directory,
       suffix: '_ignore_license',
@@ -55,7 +65,7 @@
   test(
     'Ignore packages test',
     () async {
-      for (var check in checkTypes) {
+      for (var check in Check.values) {
         await checkGolden(
           check,
           fakeGithubApi,
@@ -70,14 +80,15 @@
 }
 
 Future<void> checkGolden(
-  String check,
+  Check check,
   FakeGithubApi fakeGithubApi,
   Directory directory, {
   String suffix = '',
   List<String> ignoredLicense = const [],
   List<String> ignoredPackage = const [],
 }) async {
-  final commentPath = p.join(Directory.systemTemp.path, 'comment_$check.md');
+  final commentPath = p.join(
+      Directory.systemTemp.createTempSync().path, 'comment_${check.name}.md');
   await Health(
     directory,
     check,
@@ -94,7 +105,7 @@
   ).healthCheck();
   var comment = await File(commentPath).readAsString();
   var goldenFile =
-      File(p.join('test_data', 'golden', 'comment_$check$suffix.md'));
+      File(p.join('test_data', 'golden', 'comment_${check.name}$suffix.md'));
   if (Platform.environment['RESET_GOLDEN'] == '1') {
     goldenFile.writeAsStringSync(comment);
   } else {
diff --git a/pkgs/firehose/test_data/base_test_repo/pkgs/package5/lib/package5.dart b/pkgs/firehose/test_data/base_test_repo/pkgs/package5/lib/package5.dart
new file mode 100644
index 0000000..950f4cb
--- /dev/null
+++ b/pkgs/firehose/test_data/base_test_repo/pkgs/package5/lib/package5.dart
@@ -0,0 +1,8 @@
+/// Support for doing something awesome.
+///
+/// More dartdocs go here.
+library;
+
+export 'src/package5_base.dart' show Awesome;
+
+// TODO: Export any libraries intended for clients of this package.
diff --git a/pkgs/firehose/test_data/base_test_repo/pkgs/package5/lib/src/package5_base.dart b/pkgs/firehose/test_data/base_test_repo/pkgs/package5/lib/src/package5_base.dart
new file mode 100644
index 0000000..e8a6f15
--- /dev/null
+++ b/pkgs/firehose/test_data/base_test_repo/pkgs/package5/lib/src/package5_base.dart
@@ -0,0 +1,6 @@
+// TODO: Put public facing types in this file.
+
+/// Checks if you are awesome. Spoiler: you are.
+class Awesome {
+  bool get isAwesome => true;
+}
diff --git a/pkgs/firehose/test_data/base_test_repo/pkgs/package5/pubspec.yaml b/pkgs/firehose/test_data/base_test_repo/pkgs/package5/pubspec.yaml
new file mode 100644
index 0000000..014e843
--- /dev/null
+++ b/pkgs/firehose/test_data/base_test_repo/pkgs/package5/pubspec.yaml
@@ -0,0 +1,15 @@
+name: package5
+description: A starting point for Dart libraries or applications.
+version: 1.0.0
+# repository: https://github.com/my_org/my_repo
+
+environment:
+  sdk: ^3.0.0
+
+# Add regular dependencies here.
+dependencies:
+  # path: ^1.8.0
+
+dev_dependencies:
+  lints: ^3.0.0
+  test: ^1.24.0
diff --git a/pkgs/firehose/test_data/golden/comment_breaking.md b/pkgs/firehose/test_data/golden/comment_breaking.md
index 422549b..4bfe13d 100644
--- a/pkgs/firehose/test_data/golden/comment_breaking.md
+++ b/pkgs/firehose/test_data/golden/comment_breaking.md
@@ -9,8 +9,9 @@
 | :--- | :--- | ---: | ---: | ---: | ---: |
 |package1|None|1.0.0|1.0.0|1.0.0|:heavy_check_mark:|
 |package2|Non-Breaking|1.0.0|1.0.0|**1.1.0** <br> Got "1.0.0" expected >= "1.1.0" (non-breaking changes)|:warning:|
+|package5|Non-Breaking|1.0.0|1.2.0|1.1.0|:heavy_check_mark:|
 
 
-This check can be disabled by tagging the PR with `skip-breaking-check`
+This check can be disabled by tagging the PR with `skip-breaking-check`.
 </details>
 
diff --git a/pkgs/firehose/test_data/golden/comment_breaking_ignore_package.md b/pkgs/firehose/test_data/golden/comment_breaking_ignore_package.md
index d336860..121b540 100644
--- a/pkgs/firehose/test_data/golden/comment_breaking_ignore_package.md
+++ b/pkgs/firehose/test_data/golden/comment_breaking_ignore_package.md
@@ -8,8 +8,9 @@
 | Package | Change | Current Version | New Version | Needed Version | Looking good? |
 | :--- | :--- | ---: | ---: | ---: | ---: |
 |package2|Non-Breaking|1.0.0|1.0.0|**1.1.0** <br> Got "1.0.0" expected >= "1.1.0" (non-breaking changes)|:warning:|
+|package5|Non-Breaking|1.0.0|1.2.0|1.1.0|:heavy_check_mark:|
 
 
-This check can be disabled by tagging the PR with `skip-breaking-check`
+This check can be disabled by tagging the PR with `skip-breaking-check`.
 </details>
 
diff --git a/pkgs/firehose/test_data/golden/comment_changelog.md b/pkgs/firehose/test_data/golden/comment_changelog.md
index dfbadb2..2fae543 100644
--- a/pkgs/firehose/test_data/golden/comment_changelog.md
+++ b/pkgs/firehose/test_data/golden/comment_changelog.md
@@ -13,6 +13,6 @@
 Changes to files need to be [accounted for](https://github.com/dart-lang/ecosystem/wiki/Changelog) in their respective changelogs.
 
 
-This check can be disabled by tagging the PR with `skip-changelog-check`
+This check can be disabled by tagging the PR with `skip-changelog-check`.
 </details>
 
diff --git a/pkgs/firehose/test_data/golden/comment_changelog_ignore_package.md b/pkgs/firehose/test_data/golden/comment_changelog_ignore_package.md
index d7b7d12..00fa903 100644
--- a/pkgs/firehose/test_data/golden/comment_changelog_ignore_package.md
+++ b/pkgs/firehose/test_data/golden/comment_changelog_ignore_package.md
@@ -12,6 +12,6 @@
 Changes to files need to be [accounted for](https://github.com/dart-lang/ecosystem/wiki/Changelog) in their respective changelogs.
 
 
-This check can be disabled by tagging the PR with `skip-changelog-check`
+This check can be disabled by tagging the PR with `skip-changelog-check`.
 </details>
 
diff --git a/pkgs/firehose/test_data/golden/comment_coverage.md b/pkgs/firehose/test_data/golden/comment_coverage.md
index c594cbf..acd10c8 100644
--- a/pkgs/firehose/test_data/golden/comment_coverage.md
+++ b/pkgs/firehose/test_data/golden/comment_coverage.md
@@ -9,10 +9,11 @@
 | :--- | :--- |
 |pkgs/package1/bin/package1.dart| :broken_heart: Not covered |
 |pkgs/package2/lib/anotherLib.dart| :green_heart: 100 % |
+|pkgs/package5/lib/src/package5_base.dart| :broken_heart: Not covered |
 
 This check for [test coverage](https://github.com/dart-lang/ecosystem/wiki/Test-Coverage) is informational (issues shown here will not fail the PR).
 
 
-This check can be disabled by tagging the PR with `skip-coverage-check`
+This check can be disabled by tagging the PR with `skip-coverage-check`.
 </details>
 
diff --git a/pkgs/firehose/test_data/golden/comment_coverage_ignore_package.md b/pkgs/firehose/test_data/golden/comment_coverage_ignore_package.md
index c6bdd11..c492d00 100644
--- a/pkgs/firehose/test_data/golden/comment_coverage_ignore_package.md
+++ b/pkgs/firehose/test_data/golden/comment_coverage_ignore_package.md
@@ -1,6 +1,6 @@
-### Coverage :heavy_check_mark:
+### Coverage :warning:
 
-<details>
+<details open>
 <summary>
 Details
 </summary>
@@ -8,10 +8,11 @@
 | File | Coverage |
 | :--- | :--- |
 |pkgs/package2/lib/anotherLib.dart| :green_heart: 100 % |
+|pkgs/package5/lib/src/package5_base.dart| :broken_heart: Not covered |
 
 This check for [test coverage](https://github.com/dart-lang/ecosystem/wiki/Test-Coverage) is informational (issues shown here will not fail the PR).
 
 
-
+This check can be disabled by tagging the PR with `skip-coverage-check`.
 </details>
 
diff --git a/pkgs/firehose/test_data/golden/comment_do-not-submit.md b/pkgs/firehose/test_data/golden/comment_do-not-submit.md
index 8daf42d..4df7261 100644
--- a/pkgs/firehose/test_data/golden/comment_do-not-submit.md
+++ b/pkgs/firehose/test_data/golden/comment_do-not-submit.md
@@ -12,6 +12,6 @@
 |pkgs/package1/bin/package1.dart|
 
 
-This check can be disabled by tagging the PR with `skip-do-not-submit-check`
+This check can be disabled by tagging the PR with `skip-do-not-submit-check`.
 </details>
 
diff --git a/pkgs/firehose/test_data/golden/comment_leaking.md b/pkgs/firehose/test_data/golden/comment_leaking.md
new file mode 100644
index 0000000..64d3793
--- /dev/null
+++ b/pkgs/firehose/test_data/golden/comment_leaking.md
@@ -0,0 +1,17 @@
+### API leaks :warning:
+
+<details open>
+<summary>
+Details
+</summary>
+
+The following packages contain symbols visible in the public API, but not exported by the library. Export these symbols or remove them from your publicly visible API.
+
+| Package | Leaked API symbols |
+| :--- | :--- |
+|package5|NonExported<br>NonExported2<br>TransitiveNonExported|
+
+
+This check can be disabled by tagging the PR with `skip-leaking-check`.
+</details>
+
diff --git a/pkgs/firehose/test_data/golden/comment_leaking_ignore_package.md b/pkgs/firehose/test_data/golden/comment_leaking_ignore_package.md
new file mode 100644
index 0000000..64d3793
--- /dev/null
+++ b/pkgs/firehose/test_data/golden/comment_leaking_ignore_package.md
@@ -0,0 +1,17 @@
+### API leaks :warning:
+
+<details open>
+<summary>
+Details
+</summary>
+
+The following packages contain symbols visible in the public API, but not exported by the library. Export these symbols or remove them from your publicly visible API.
+
+| Package | Leaked API symbols |
+| :--- | :--- |
+|package5|NonExported<br>NonExported2<br>TransitiveNonExported|
+
+
+This check can be disabled by tagging the PR with `skip-leaking-check`.
+</details>
+
diff --git a/pkgs/firehose/test_data/golden/comment_license.md b/pkgs/firehose/test_data/golden/comment_license.md
index a53ef1c..ad5231b 100644
--- a/pkgs/firehose/test_data/golden/comment_license.md
+++ b/pkgs/firehose/test_data/golden/comment_license.md
@@ -15,6 +15,7 @@
 | :--- |
 |pkgs/package1/bin/package1.dart|
 |pkgs/package2/lib/anotherLib.dart|
+|pkgs/package5/lib/src/package5_base.dart|
 
 All source files should start with a [license header](https://github.com/dart-lang/ecosystem/wiki/License-Header).
 
@@ -32,11 +33,12 @@
 |pkgs/package3/bin/package3.dart|
 |pkgs/package3/lib/package3.dart|
 |pkgs/package3/test/package3_test.dart|
+|pkgs/package5/lib/package5.dart|
 </details>
 
 
 
 
-This check can be disabled by tagging the PR with `skip-license-check`
+This check can be disabled by tagging the PR with `skip-license-check`.
 </details>
 
diff --git a/pkgs/firehose/test_data/golden/comment_license_ignore_license.md b/pkgs/firehose/test_data/golden/comment_license_ignore_license.md
index 4fa23f1..cfb1871 100644
--- a/pkgs/firehose/test_data/golden/comment_license_ignore_license.md
+++ b/pkgs/firehose/test_data/golden/comment_license_ignore_license.md
@@ -15,6 +15,7 @@
 | :--- |
 |pkgs/package1/bin/package1.dart|
 |pkgs/package2/lib/anotherLib.dart|
+|pkgs/package5/lib/src/package5_base.dart|
 
 All source files should start with a [license header](https://github.com/dart-lang/ecosystem/wiki/License-Header).
 
@@ -29,11 +30,12 @@
 |pkgs/package1/test/package1_test.dart|
 |pkgs/package2/lib/package2.dart|
 |pkgs/package2/test/package2_test.dart|
+|pkgs/package5/lib/package5.dart|
 </details>
 
 
 
 
-This check can be disabled by tagging the PR with `skip-license-check`
+This check can be disabled by tagging the PR with `skip-license-check`.
 </details>
 
diff --git a/pkgs/firehose/test_data/golden/comment_license_ignore_package.md b/pkgs/firehose/test_data/golden/comment_license_ignore_package.md
index 59c637d..47860a9 100644
--- a/pkgs/firehose/test_data/golden/comment_license_ignore_package.md
+++ b/pkgs/firehose/test_data/golden/comment_license_ignore_package.md
@@ -14,6 +14,7 @@
 | Files |
 | :--- |
 |pkgs/package2/lib/anotherLib.dart|
+|pkgs/package5/lib/src/package5_base.dart|
 
 All source files should start with a [license header](https://github.com/dart-lang/ecosystem/wiki/License-Header).
 
@@ -29,11 +30,12 @@
 |pkgs/package3/bin/package3.dart|
 |pkgs/package3/lib/package3.dart|
 |pkgs/package3/test/package3_test.dart|
+|pkgs/package5/lib/package5.dart|
 </details>
 
 
 
 
-This check can be disabled by tagging the PR with `skip-license-check`
+This check can be disabled by tagging the PR with `skip-license-check`.
 </details>
 
diff --git a/pkgs/firehose/test_data/golden/comment_version.md b/pkgs/firehose/test_data/golden/comment_version.md
index c03092a..e01840e 100644
--- a/pkgs/firehose/test_data/golden/comment_version.md
+++ b/pkgs/firehose/test_data/golden/comment_version.md
@@ -10,10 +10,11 @@
 | package:package1 | 1.0.0 | (error) pub publish dry-run failed; add the `publish-ignore-warnings` label to ignore |
 | package:package2 | 1.0.0 | (error) pub publish dry-run failed; add the `publish-ignore-warnings` label to ignore |
 | package:package3 | 1.0.0 | (error) pub publish dry-run failed; add the `publish-ignore-warnings` label to ignore |
+| package:package5 | 1.2.0 | (error) pubspec version (1.2.0) and changelog (null) don't agree |
 
 Documentation at https://github.com/dart-lang/ecosystem/wiki/Publishing-automation.
     
 
-This check can be disabled by tagging the PR with `skip-version-check`
+This check can be disabled by tagging the PR with `skip-version-check`.
 </details>
 
diff --git a/pkgs/firehose/test_data/golden/comment_version_ignore_package.md b/pkgs/firehose/test_data/golden/comment_version_ignore_package.md
index 17ab16e..697af42 100644
--- a/pkgs/firehose/test_data/golden/comment_version_ignore_package.md
+++ b/pkgs/firehose/test_data/golden/comment_version_ignore_package.md
@@ -9,10 +9,11 @@
 | :--- | ---: | :--- |
 | package:package2 | 1.0.0 | (error) pub publish dry-run failed; add the `publish-ignore-warnings` label to ignore |
 | package:package3 | 1.0.0 | (error) pub publish dry-run failed; add the `publish-ignore-warnings` label to ignore |
+| package:package5 | 1.2.0 | (error) pubspec version (1.2.0) and changelog (null) don't agree |
 
 Documentation at https://github.com/dart-lang/ecosystem/wiki/Publishing-automation.
     
 
-This check can be disabled by tagging the PR with `skip-version-check`
+This check can be disabled by tagging the PR with `skip-version-check`.
 </details>
 
diff --git a/pkgs/firehose/test_data/test_repo/pkgs/package5/lib/package5.dart b/pkgs/firehose/test_data/test_repo/pkgs/package5/lib/package5.dart
new file mode 100644
index 0000000..950f4cb
--- /dev/null
+++ b/pkgs/firehose/test_data/test_repo/pkgs/package5/lib/package5.dart
@@ -0,0 +1,8 @@
+/// Support for doing something awesome.
+///
+/// More dartdocs go here.
+library;
+
+export 'src/package5_base.dart' show Awesome;
+
+// TODO: Export any libraries intended for clients of this package.
diff --git a/pkgs/firehose/test_data/test_repo/pkgs/package5/lib/src/package5_base.dart b/pkgs/firehose/test_data/test_repo/pkgs/package5/lib/src/package5_base.dart
new file mode 100644
index 0000000..fcca7d7
--- /dev/null
+++ b/pkgs/firehose/test_data/test_repo/pkgs/package5/lib/src/package5_base.dart
@@ -0,0 +1,17 @@
+// TODO: Put public facing types in this file.
+
+/// Checks if you are awesome. Spoiler: you are.
+class Awesome {
+  bool get isAwesome => true;
+
+  NonExported get myClass => NonExported();
+  NonExported2 get myClass2 => NonExported2();
+}
+
+class NonExported {}
+
+class NonExported2 {
+  TransitiveNonExported get myClass => TransitiveNonExported();
+}
+
+class TransitiveNonExported {}
diff --git a/pkgs/firehose/test_data/test_repo/pkgs/package5/pubspec.yaml b/pkgs/firehose/test_data/test_repo/pkgs/package5/pubspec.yaml
new file mode 100644
index 0000000..10fbd15
--- /dev/null
+++ b/pkgs/firehose/test_data/test_repo/pkgs/package5/pubspec.yaml
@@ -0,0 +1,15 @@
+name: package5
+description: A starting point for Dart libraries or applications.
+version: 1.2.0
+# repository: https://github.com/my_org/my_repo
+
+environment:
+  sdk: ^3.0.0
+
+# Add regular dependencies here.
+dependencies:
+  # path: ^1.8.0
+
+dev_dependencies:
+  lints: ^3.0.0
+  test: ^1.24.0