Skip to content

Expo config plugin: programmatically merge backup-rules exclusions into host app manifest #46

@gmaclennan

Description

@gmaclennan

Context

@comapeo/core-react-native is published as an Expo module. The
library currently declares android:dataExtractionRules and
android:fullBackupContent on its <application> element to exclude
the rootkey-bearing SharedPreferences from cloud backup and
device-to-device transfer (see android/src/main/AndroidManifest.xml,
PR #36, comment thread on backup-rules merge conflict).

Host apps that already declare these attributes get a manifest-merger
conflict and must manually:

  1. Merge the library's exclusions into their own backup-rules XML.
  2. Add tools:replace="android:dataExtractionRules,android:fullBackupContent" to their <application> tag.

This is documented in README.md > Configure for Android > Backup-rules merge conflict but it's still a friction point at integration time
and easy to forget when iterating.

Proposal

An Expo config plugin that runs at expo prebuild time and:

  1. Adds the library's backup-rules XML files to the host app's
    res/xml/ directory
    (or makes them resolvable via the library
    AAR — they're already there, but the plugin can verify).
  2. Reads the host app's existing backup-rules XML if any.
    • Default Expo template apps don't set these → plugin sets the
      attributes on the host's <application> and copies our XML files
      in. No conflict because the host had no prior declaration.
    • Host already has rules → plugin parses the host's XML, merges our
      <exclude domain="sharedpref" path="comapeo-core.xml" /> entries
      into the right sections (<cloud-backup>, <device-transfer>,
      <full-backup-content>), writes the merged XML back, and ensures
      the <application> attributes still point at it.
  3. Removes the attributes from the library's own
    AndroidManifest.xml
    so there's never a merge conflict —
    responsibility shifts entirely to the host manifest, which the
    plugin owns at prebuild time.

For non-Expo (bare-RN-without-Expo) consumers, the README path stays
the canonical fix.

Why this is worth doing

  • Eliminates a build-time integration step for the largest class of
    consumers (managed-Expo apps).
  • Removes the manifest-merger conflict for any consumer that has its
    own backup rules — friction point disappears entirely.
  • Keeps secure-by-default: forgetting to register the plugin is the
    only way to lose the exclusion, and that's discoverable via a
    smoke test (the encrypted SharedPreferences ends up in backups).

Cost

Roughly 100–150 LOC of plugin code plus tests:

  • withDangerousMod for the XML file copy / merge step.
  • withAndroidManifest for the <application> attribute add /
    tools:replace handling.
  • An XML-merge helper that's safe against repeated invocations
    (expo prebuild runs are idempotent in the rest of the toolchain;
    this plugin should be too).
  • A test fixture covering: (a) host with no rules, (b) host with
    rules that already have our exclusions, (c) host with rules that
    conflict.

Acceptance

  • Plugin lives in plugins/ (or wherever published Expo plugins
    go in this repo) and is auto-registered when the package is
    installed.
  • expo prebuild --clean on a fresh Expo template + this module
    produces a host manifest with our exclusions, no conflicts.
  • expo prebuild --clean on an Expo template that ALREADY has
    backup rules merges our exclusions in non-destructively.
  • Re-running prebuild is idempotent — no duplicate <exclude>
    entries, no toggling tools:replace.
  • Library's own AndroidManifest.xml no longer declares the
    backup attributes once the plugin path is verified end-to-end.
  • README.md > Configure for Android updates to reflect that
    Expo consumers need do nothing; bare-RN consumers follow the
    manual path.

Open questions

  1. Should the plugin be opt-in (consumer adds it to their app.json
    plugins array) or auto-registered (via expo-module.config.json)?
    Auto is friendlier; opt-in is more explicit.
  2. What should the plugin do if it detects an existing
    tools:replace="android:dataExtractionRules" on the host? Mostly:
    nothing, because the host has explicitly opted out of merging.
    Log a warning suggesting they include our exclusions.
  3. How do bare-RN consumers (no Expo) interact with this? They
    continue to follow the README path; the plugin doesn't apply.

Metadata

Metadata

Assignees

No one assigned

    Labels

    low priorityLow priority issue, possible wontfix candidate

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions