Skip to main content

storehound 0.2.0 copy "storehound: ^0.2.0" to clipboard
storehound: ^0.2.0 copied to clipboard

Catch App Store and Google Play rejections in your Flutter project before review. A Dart CLI that lints Info.plist, privacy manifests, entitlements and AndroidManifest in CI.

storehound #

Catch App Store and Google Play rejections in your Flutter project before you upload: one Dart command lints Info.plist, the privacy manifest, entitlements and AndroidManifest.xml, and fails CI on the mistakes that get builds refused.

pub version pub points pub likes CI license: MIT

Why #

  • A missing purpose string or a privacy manifest that never reaches the bundle compiles, passes every test, and only shows up as an ITMS- email after upload.
  • A vague purpose string or an unjustified background mode costs you a whole review cycle.
  • Play refuses an update below the target API deadline, usually during an urgent fix.

It reads the files you ship and answers in about a second. No Xcode, no macOS runner, no build.

Install #

dart pub global activate storehound

Or pin it per project with flutter pub add --dev storehound and run dart run storehound.

Coming from imaji_store_preflight #

This package was renamed. Replace imaji_store_preflight with storehound in dev_dependencies (or dart pub global activate storehound), run storehound instead of imaji_store_preflight, and rename imaji_store_preflight.yaml to storehound.yaml. Rules and options are unchanged.

Quick start #

cd my_flutter_app
storehound

If ~/.pub-cache/bin is not on your PATH, run dart pub global run storehound. Exit code 0 means clean, 1 means findings at or above --fail-on (default error), 2 means a usage or configuration problem.

Example output #

A fresh flutter create app gets one warning: no privacy manifest yet. Here it is after flutter pub add image_picker and a one-line camera purpose string (--brief drops the why/fix text):

$ storehound --brief
privacy-manifest-present — The app target ships a PrivacyInfo.xcprivacy
  warning  ios/Runner/Info.plist
    No privacy manifest at ios/Runner/PrivacyInfo.xcprivacy (or the path
    configured under `paths.privacy_manifest`).

purpose-string-missing — Linked plugins have the purpose strings they need
  error  ios/Runner/Info.plist
    `NSPhotoLibraryUsageDescription` is missing, but image_picker needs it.
  error  ios/Runner/Info.plist
    `NSMicrophoneUsageDescription` is missing, but image_picker needs it.

purpose-string-quality — Purpose strings name a feature and give an example, not restate the permission
  error  ios/Runner/Info.plist:5
    `NSCameraUsageDescription` only restates the permission; name the feature
    that uses it and give an example. Current text: "This app needs camera
    access."

✗ 15 rules checked: 3 errors, 1 warning, 0 info. Failing: findings at or above `error`.

The exit code is 1, so CI fails. Without --brief each finding also says why it matters, how to fix it, and links Apple's or Google's documentation. --format json and --sarif give the same findings in machine-readable form.

Run it in CI #

This job fails the build on errors and shows each finding as a code scanning annotation on the pull request:

name: Store preflight
on: [push, pull_request]

jobs:
  preflight:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write   # upload SARIF
    steps:
      - uses: actions/checkout@v6
      - uses: dart-lang/setup-dart@v1
      - run: dart pub global activate storehound '^0.2.0'
      - run: dart pub global run storehound --sarif --output preflight.sarif
      - uses: github/codeql-action/upload-sarif@v4
        if: always()           # upload even when the check fails the job
        with:
          sarif_file: preflight.sarif
          category: storehound

No macOS runner needed. Code scanning needs a public repository or GitHub Advanced Security; without it, drop the upload step and read the text output in the job log. More on CI, including how android-target-sdk behaves on a Dart-only runner.

What it checks #

Rule Catches
purpose-string-missing ITMS-90683 "Missing purpose string" for plugins like image_picker, camera, geolocator
purpose-string-quality Guideline 5.1.1(ii): a purpose string that only restates the permission
privacy-manifest-present ITMS-91053: no PrivacyInfo.xcprivacy, or one missing from Copy Bundle Resources
privacy-manifest-values Data types, purposes and reason codes that are not Apple's exact values
privacy-manifest-tracking NSPrivacyTracking contradicting the entries or tracking domains
privacy-manifest-sdk-data "Collects nothing" while Firebase, AdMob, Sentry or another known SDK is linked
tracking-usage-description "Add for Review" refused over NSUserTrackingUsageDescription vs declared tracking
background-modes-justified Guideline 2.5.4: background modes with no feature behind them
push-entitlement Push SDK linked without aps-environment, so notifications never arrive
export-options-build-number Xcode silently bumping CFBundleVersion at export
bundle-localizations CFBundleLocalizations out of sync with your ARB locales
android-target-sdk targetSdk below Google Play's current deadline
android-16kb-page-size A build toolchain without 16 KB page-size support
android-picker-queries file_picker without an OPEN_DOCUMENT <queries> entry
unreadable-file A config file that does not parse, so rules would silently pass

storehound --explain <rule-id> prints the full why, fix, options and documentation links for one rule.

Is it noisy on a real app? #

It should not be. CI runs it with --fail-on info on a production-shaped fixture (two flavors, SwiftPM only, AGP 9, separate Debug and Release entitlements, a large commented Info.plist) and expects no findings at all, and the tests change one thing at a time in it to check that each mistake is still caught. If it reports something on your app that is not a real problem, please open an issue with an anonymised copy of the lines involved.

Documentation #

Contributing #

The most useful contributions are a rejection you hit that no rule catches (open an issue with the anonymised reviewer wording or ITMS- code and the public Apple or Google documentation) and corrections to the data tables. CONTRIBUTING.md explains how to add a rule; issues labelled good first issue are a good place to start. Questions and ideas that are not bugs go to Discussions.

Trademarks #

Apple, App Store, iOS and Xcode are trademarks of Apple Inc. Google Play and Android are trademarks of Google LLC. This project is independent and is not affiliated with, sponsored by or endorsed by Apple or Google; the names are used only to say which store rules a check is about.

License #

MIT © saimskywalker. See LICENSE.

0
likes
160
points
439
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Catch App Store and Google Play rejections in your Flutter project before review. A Dart CLI that lints Info.plist, privacy manifests, entitlements and AndroidManifest in CI.

Repository (GitHub)
View/report issues
Contributing

Topics

#flutter #ios #android #app-store #ci

License

MIT (license)

Dependencies

args, path, xml, yaml

More

Packages that depend on storehound